Spring Boot 3.x+Spring Security 6.x OAuth2客户端兼资源服务器(会话Cookie模式)返回403问题排查
看起来你遇到的核心问题是两个SecurityFilterChain的匹配逻辑冲突,导致请求还没到达资源服务器的FilterChain就被拒绝了,另外资源服务器的配置也需要适配会话Cookie的认证方式。我来一步步帮你分析和解决:
问题根源分析
FilterChain顺序与路径覆盖问题
你的clientFilterChain设置了@Order(1)(优先级更高),但没有限定它处理的请求路径范围。这意味着所有请求都会先经过这个FilterChain,而它的authorizeHttpRequests只配置了/api/user/current需要认证,其他路径没有匹配规则——Spring Security默认会对未匹配的请求执行拒绝访问,所以哪怕你在第二个FilterChain里设置permitAll,请求根本到不了那里,直接返回403。资源服务器未适配会话认证
即使解决了FilterChain的路径问题,资源服务器默认只接受请求头中的Bearer Token,但你的客户端是用会话Cookie来请求的,资源服务器无法识别会话中的OAuth2认证信息,也会导致403。
解决方案
步骤1:限定客户端FilterChain的处理范围
修改clientFilterChain,添加requestMatcher让它只处理登录、OAuth2回调和指定的用户接口,其他路径交给资源服务器FilterChain处理:
@Order(1) @Bean public SecurityFilterChain clientFilterChain(HttpSecurity httpSecurity) throws Exception { return httpSecurity // 只处理登录相关路径和/user/current接口 .requestMatcher( new AntPathRequestMatcher("/login/**"), new AntPathRequestMatcher("/oauth2/**"), new AntPathRequestMatcher("/api/user/current") ) .cors(Customizer.withDefaults()) .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(matcherRegistry -> matcherRegistry // 登录和OAuth2回调路径允许匿名访问 .requestMatchers("/login/**", "/oauth2/**").permitAll() // /api/user/current需要认证 .requestMatchers("/api/user/current").authenticated() ) .oauth2Login(loginConfigurer -> loginConfigurer.defaultSuccessUrl(applicationConfiguration.getFrontendRedirectUrl(), true)) .exceptionHandling(exceptionHandlingConfigurer -> exceptionHandlingConfigurer.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))) .logout(logoutConfigurer -> logoutConfigurer.logoutSuccessHandler(logoutSuccessHandler())) .build(); }
步骤2:适配资源服务器的会话认证
修改资源服务器FilterChain,允许会话认证,并确保它能识别会话中的OAuth2认证信息:
@Order(2) @Bean public SecurityFilterChain resourceServerFilterChain(HttpSecurity httpSecurity) throws Exception { return httpSecurity .cors(Customizer.withDefaults()) .csrf(AbstractHttpConfigurer::disable) // 允许创建/使用会话,适配Cookie认证 .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)) .authorizeHttpRequests(matcherRegistry -> matcherRegistry.anyRequest().authenticated()) .oauth2ResourceServer(configurer -> configurer.jwt(Customizer.withDefaults())) // 配置会话认证支持,让资源服务器能识别OAuth2登录后的会话信息 .oauth2Login(Customizer.withDefaults()) .build(); }
额外检查点
- 确保你的JWT配置正确:比如Keycloak的JWKS端点(
spring.security.oauth2.resourceserver.jwt.jwk-set-uri)配置无误,否则JWT验证失败也会返回403。 - 检查会话中的认证信息:可以在资源接口中打印
SecurityContextHolder.getContext().getAuthentication(),确认是否能获取到OAuth2AuthenticationToken或JwtAuthenticationToken。
这样调整后,请求/api/test-data会先经过第二个FilterChain,并且能识别会话中的认证信息,正常处理资源请求。
备注:内容来源于stack exchange,提问作者Robert Strauch




