带有 Spring Security 的 Spring Cloud Gateway,用于使用 JWT 身份验证进行注册/登录。 403 禁止错误

问题描述 投票:0回答:1

我正在为一个微服务应用项目学习SpringBoot。 我将 Keycloak 客户端身份验证路由与服务帐户角色结合使用,并将 keycloak 配置添加到 API 网关服务 application.properties 中。 这是 API 网关安全配置:


@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {


    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity serverHttpSecurity){

        serverHttpSecurity
                .authorizeExchange(exchange -> exchange
                        .pathMatchers(
                                "/api/**",
                                "/eureka/**"
                        )
                        .permitAll()
                        .anyExchange()
                        .authenticated())
                .csrf()
                .disable()

                .oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::jwt);
        return serverHttpSecurity.build();
    }

}

现在我正在创建一个身份服务应用程序,除了用于用户管理的用户服务之外,它还处理用户注册和登录。该服务将 JWT 身份验证与 Spring Security AuthenticationProvider、JwtFilter 和 UserNamePasswordAuthenticationFilter 结合使用。 以下是身份服务安全配置。


@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfig {


    private final JwtFilter jwtAuthFilter;
    private final AuthenticationProvider authenticationProvider;


    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{
        httpSecurity
                .cors(withDefaults())
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeRequests(request -> request.regexMatchers(
                        "/api/auth/*",
                        "/v2/api-docs",
                        "/v3/api-docs",
                        "/v3/api-docs/*",
                                "/swagger-resources/",
                                "/swagger-resources/*",
                                "/configuration/ui",
                                "/configuration/security",
                                "/swagger-ui/",
                                "/webjars/*",
                                "/swagger-ui.html"
                        )
                        .permitAll()
                        .anyRequest()
                        .authenticated()
                )
                .sessionManagement(
                        session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                )
                .authenticationProvider(authenticationProvider)
                .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

        return httpSecurity.build();
    }
}

注册/本地主机 这是端点,在使用带有 JWT 令牌的 OAuth2.0 执行邮递员 POST 请求时,我收到 403: Forbidden 作为响应。 以下是 API 网关服务的日志:

2024-04-12 20:15:02.924 TRACE [xpense-gateway,,] 35685 --- [     parallel-8] o.s.c.g.h.p.PathRoutePredicateFactory    : Pattern "[/eureka/web]" does not match against value "/api/auth/register"
2024-04-12 20:15:02.924 TRACE [xpense-gateway,,] 35685 --- [     parallel-8] o.s.c.g.h.p.PathRoutePredicateFactory    : Pattern "/api/auth/**" matches against value "/api/auth/register"
2024-04-12 20:15:02.924 DEBUG [xpense-gateway,,] 35685 --- [     parallel-8] o.s.c.g.h.RoutePredicateHandlerMapping   : Route matched: identity-service
2024-04-12 20:15:02.924 DEBUG [xpense-gateway,,] 35685 --- [     parallel-8] o.s.c.g.h.RoutePredicateHandlerMapping   : Mapping [Exchange: POST http://localhost:8080/api/auth/register] to Route{id='identity-service', uri=lb://identity-service, order=0, predicate=Paths: [/api/auth/**], match trailing slash: true, gatewayFilters=[[[SpringCloudCircuitBreakerResilience4JFilterFactory name = 'resilience', fallback = /identity-fallback], order = 0]], metadata={}}
2024-04-12 20:15:02.924 DEBUG [xpense-gateway,,] 35685 --- [     parallel-8] o.s.c.g.h.RoutePredicateHandlerMapping   : [ade0fee6-4] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler@56568494
2024-04-12 20:15:02.924 DEBUG [xpense-gateway,,] 35685 --- [     parallel-8] o.s.c.g.handler.FilteringWebHandler      : Sorted gatewayFilterFactories: [[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter@7a3a49e5}, order = -2147483648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@305881b8}, order = -2147482648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@6dbb3d7d}, order = -1], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardPathFilter@3ea9a091}, order = 0], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.GatewayMetricsFilter@26495639}, order = 0], [[SpringCloudCircuitBreakerResilience4JFilterFactory name = 'resilience', fallback = /identity-fallback], order = 0], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@6c1b82cd}, order = 10000], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter@54687fd0}, order = 10150], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.LoadBalancerServiceInstanceCookieFilter@6eaf030c}, order = 10151], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@16f4a3c0}, order = 2147483646], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyRoutingFilter@b2da3a5}, order = 2147483647], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardRoutingFilter@acd3460}, order = 2147483647]]
2024-04-12 20:15:02.924 TRACE [xpense-gateway,ffa6cb10b03275d4,5984f380554abaa0] 35685 --- [     parallel-8] o.s.c.g.filter.RouteToRequestUrlFilter   : RouteToRequestUrlFilter start
2024-04-12 20:15:02.924 TRACE [xpense-gateway,ffa6cb10b03275d4,5984f380554abaa0] 35685 --- [     parallel-8] s.c.g.f.ReactiveLoadBalancerClientFilter : ReactiveLoadBalancerClientFilter url before: lb://identity-service/api/auth/register
2024-04-12 20:15:02.925 TRACE [xpense-gateway,ffa6cb10b03275d4,5984f380554abaa0] 35685 --- [     parallel-8] s.c.g.f.ReactiveLoadBalancerClientFilter : LoadBalancerClientFilter url chosen: http://VFIEVOX3.Router:35185/api/auth/register
2024-04-12 20:15:02.945 TRACE [xpense-gateway,,] 35685 --- [r-http-epoll-11] o.s.c.gateway.filter.NettyRoutingFilter  : outbound route: dc99dbca, inbound: [ade0fee6-4] 
2024-04-12 20:15:02.951 TRACE [xpense-gateway,,] 35685 --- [r-http-epoll-11] o.s.c.g.filter.NettyWriteResponseFilter  : NettyWriteResponseFilter start inbound: dc99dbca, outbound: [ade0fee6-4] 
2024-04-12 20:15:02.952 TRACE [xpense-gateway,,] 35685 --- [r-http-epoll-11] o.s.c.g.filter.GatewayMetricsFilter      : spring.cloud.gateway.requests tags: [tag(httpMethod=POST),tag(httpStatusCode=403),tag(outcome=CLIENT_ERROR),tag(routeId=identity-service),tag(routeUri=lb://identity-service),tag(status=FORBIDDEN)]

我知道 KeyCloak 机制可以处理用户身份验证,但我正在尝试从头开始在服务中构建自己的机制来处理用户身份验证和授权。

我尝试浏览 Spring Security 的文档,但现在我很困惑,因为我不清楚 SecurityFilterChain 和 SecurityWebFilterChain 到底如何。 SecurityWebFilterChain 为 Oauth2ResourceServer 提供 ServerHttpSecurity,但它不处理我期待实现的 SessionManagement 和 sessionCreation。

提前谢谢您。

spring-boot spring-security keycloak spring-cloud spring-cloud-gateway
1个回答
0
投票

代码配置中的问题与Web过滤器链机制的顺序有关。

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{
    httpSecurity
            .authorizeRequests(request -> request.regexMatchers(
                    "/api/auth/*",
                    "/v2/api-docs",
                    "/v3/api-docs",
                    "/v3/api-docs/*",
                            "/swagger-resources/",
                            "/swagger-resources/*",
                            "/configuration/ui",
                            "/configuration/security",
                            "/swagger-ui/",
                            "/webjars/*",
                            "/swagger-ui.html"
                    )
                    .permitAll()
                    .anyRequest()
                    .authenticated()
            )
            .cors(withDefaults())
            .csrf(AbstractHttpConfigurer::disable)
            .sessionManagement(
                    session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .authenticationProvider(authenticationProvider)
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

    return httpSecurity.build();
}

由于authorizeRequests未正确配置,而是cors(withDefaults())定制器在原始配置中使用csrf()进行初始化。 这导致了模式匹配请求的身份验证允许和权限。

© www.soinside.com 2019 - 2024. All rights reserved.