You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 00:22:12