Spring Security 6自定义AuthenticationFilter无法跳转OAuth2重定向页问题
问题背景
为获取登录表单的额外自定义字段,在Spring Security 6中自定义了继承UsernamePasswordAuthenticationFilter的CustomAuthenticationFilter,以及继承UsernamePasswordAuthenticationToken的CustomAuthenticationToken。认证逻辑正常(AuthenticationProvider的authenticate方法能返回已认证的Authentication对象),但认证后停留在登录页,不会跳转到OAuth2客户端的重定向页面;不使用自定义过滤器时功能完全正常。
配置代码
安全配置类
@EnableWebSecurity @Configuration(proxyBeanMethods = false) public class AuthenticationSecurityConfig extends AbstractHttpConfigurer<AuthenticationSecurityConfig, HttpSecurity> { @Override public void configure(HttpSecurity http) throws Exception { AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class); http.addFilterBefore(authenticationFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class); } @Bean SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests() .requestMatchers("/css/**", "/login") .permitAll() .requestMatchers("/resources/**") .permitAll() .and() .formLogin(form -> form .loginPage("/login") .permitAll() ) .logout() .logoutUrl("/logout") .and() .authorizeHttpRequests( auth -> auth.anyRequest().authenticated() ) .apply(securityConfig()); return http.build(); } }
项目依赖配置(build.gradle)
plugins { id "org.springframework.boot" version "3.0.0-RC2" id "io.spring.dependency-management" version "1.0.11.RELEASE" id "java" }
implementation "org.springframework.security:spring-security-oauth2-authorization-server:1.0.0"
关键日志信息
.HttpSessionRequestCache : Saved request http://192.168.0.107:9000/oauth2/authorize?client_id=messaging-client&redirect_uri=http%3A%2F%2F192.168.0.107%3A8080%2Fauth%2Fsigninwin%2Fmain&response_type=code&scope=openid%20profile%20message.read&state=802af0dcd82a483eb726c1dffff0867d&code_challenge=t_tfBjZPRd228uEZuQJ56clfXokGYqiwkudQqKhWQqo&code_challenge_method=S256&prompt=login&response_mode=query&continue&continue to session
2022-11-30T19:48:05.063+08:00 DEBUG 63801 --- [nio-9000-exec-5] o.s.s.web.DefaultRedirectStrategy : Redirecting to http://192.168.0.107:9000/login
2022-11-30T19:48:05.078+08:00 DEBUG 63801 --- [nio-9000-exec-6] o.s.security.web.FilterChainProxy : Securing GET /login
2022-11-30T19:48:05.078+08:00 DEBUG 63801 --- [nio-9000-exec-6] o.s.security.web.FilterChainProxy : Secured GET /login
2022-11-30T19:48:05.084+08:00 DEBUG 63801 --- [nio-9000-exec-6] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
解决方案
问题根源是自定义过滤器未正确处理请求缓存(RequestCache)和认证成功后的重定向逻辑,同时过滤器添加方式引发流程冲突,修复步骤如下:
1. 完善自定义过滤器的认证成功逻辑
在CustomAuthenticationFilter中注入RequestCache,实现认证成功后跳转至缓存的授权请求地址:
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private final RequestCache requestCache = new HttpSessionRequestCache(); public CustomAuthenticationFilter(AuthenticationManager authenticationManager) { super(authenticationManager); // 匹配formLogin配置的登录请求路径 setFilterProcessesUrl("/login"); } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { // 获取之前缓存的授权请求 SavedRequest savedRequest = this.requestCache.getRequest(request, response); if (savedRequest != null) { // 移除缓存请求,避免重复跳转 this.requestCache.removeRequest(request, response); // 跳转到原始授权地址 getRedirectStrategy().sendRedirect(request, response, savedRequest.getRedirectUrl()); } else { // 无缓存请求时执行默认逻辑 super.successfulAuthentication(request, response, chain, authResult); } } }
2. 替换默认过滤器而非添加前置过滤器
原配置中addFilterBefore会导致同时存在两个登录过滤器,引发流程冲突,修改配置类的configure方法:
@Override public void configure(HttpSecurity http) throws Exception { AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class); // 替换默认的UsernamePasswordAuthenticationFilter http.replaceFilter(new CustomAuthenticationFilter(authenticationManager)); }
核心原因
默认的UsernamePasswordAuthenticationFilter会自动处理Spring Security的RequestCache:当用户访问受保护的/oauth2/authorize端点时,请求会被缓存,认证成功后自动跳转至缓存地址。自定义过滤器缺失该逻辑会导致停留在登录页;同时添加前置过滤器会让默认过滤器依然存在,干扰认证流程。
内容的提问来源于stack exchange,提问作者stephen yup

