Spring Boot 3.3.0自定义Referer认证空指针及多认证共存问题
问题解决指南
一、解决AuthenticationManager空指针异常
空指针是因为RefererAuthenticationFilter未正确获取AuthenticationManager,可通过以下两种方式修复:
方法1:让过滤器实现AuthenticationManagerAware接口
修改过滤器类,实现接口并重写注入方法:
public class RefererAuthenticationFilter extends OncePerRequestFilter implements AuthenticationManagerAware { private AuthenticationManager authenticationManager; @Override public void setAuthenticationManager(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; } // 其他过滤逻辑实现 }
方法2:在SecurityConfig中手动注入
通过配置类获取AuthenticationManager并设置到过滤器:
@Configuration @EnableWebSecurity public class SecurityConfig { private final AuthenticationManager authenticationManager; public SecurityConfig(AuthenticationConfiguration authConfig) throws Exception { this.authenticationManager = authConfig.getAuthenticationManager(); } @Bean public RefererAuthenticationFilter refererAuthenticationFilter() { RefererAuthenticationFilter filter = new RefererAuthenticationFilter(); filter.setAuthenticationManager(authenticationManager); return filter; } // 其他配置逻辑 }
二、让自定义认证提供者优先执行
在AuthenticationManager中优先注册自定义提供者,保证其先于Okta的提供者执行:
@Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { AuthenticationManagerBuilder builder = authConfig.getAuthenticationManagerBuilder(); // 先添加自定义提供者 builder.authenticationProvider(new RefererAuthenticationProvider()); // Okta的提供者会自动注册,无需手动添加 return builder.build(); }
三、在Okta过滤器链中插入自定义过滤器
将自定义过滤器放在Okta相关过滤器之前,确保iframe请求先被处理:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http // 将自定义过滤器放在Okta授权请求重定向过滤器之前 .addFilterBefore(refererAuthenticationFilter(), OAuth2AuthorizationRequestRedirectFilter.class) // 保留Okta的OAuth2登录配置 .oauth2Login(withDefaults()) .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ); return http.build(); }
四、完善Authentication对象携带用户信息
创建自定义Authentication类,封装id、name、email参数:
public class RefererAuthenticationToken extends AbstractAuthenticationToken { private final String userId; private final String name; private final String email; // 未认证状态构造方法 public RefererAuthenticationToken(String userId, String name, String email) { super(Collections.emptyList()); this.userId = userId; this.name = name; this.email = email; setAuthenticated(false); } // 已认证状态构造方法 public RefererAuthenticationToken(String userId, String name, String email, Collection<? extends GrantedAuthority> authorities) { super(authorities); this.userId = userId; this.name = name; this.email = email; setAuthenticated(true); } @Override public Object getCredentials() { return null; // 无密码类凭证,返回null } @Override public Object getPrincipal() { return userId; } // 提供getter方法供服务和Thymeleaf调用 public String getName() { return name; } public String getEmail() { return email; } }
在RefererAuthenticationProvider的authenticate方法中,验证通过后返回已认证的Token:
@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { RefererAuthenticationToken token = (RefererAuthenticationToken) authentication; // 执行Referer和Sec-Fetch-Dest的验证逻辑 // 验证通过后生成已认证Token return new RefererAuthenticationToken( token.getPrincipal().toString(), token.getName(), token.getEmail(), Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")) ); }
Thymeleaf中可直接通过${authentication.name}、${authentication.email}获取用户信息,服务端则通过SecurityContextHolder.getContext().getAuthentication()获取Token。
五、兼容性说明
自定义Referer认证提供者与Okta的认证提供者可以共存于同一AuthenticationManager。请求到来时,AuthenticationManager会按注册顺序依次调用提供者,第一个能处理当前Authentication的提供者会执行逻辑,后续提供者不会被触发。因此优先注册自定义提供者,就能让iframe首次请求被优先处理,其他请求自动交给Okta的认证逻辑。
内容的提问来源于stack exchange,提问作者Steve Maring
相关产品推荐
相关产品推荐

