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

配置Spring Security多重叠AuthenticationProvider遇认证问题求助

解决Spring Security多认证流程冲突问题

针对请求头携带两种不同token(webAuth/hardwareAuth)的多认证场景,现有配置存在认证提供者交叉执行、流程触发逻辑混乱的问题,可通过以下步骤修复:

1. 定义专属Authentication Token类

为两种认证流程创建独立的Token类型,避免不同Provider处理同一Token导致的冲突:

// Web认证专属Token
public class WebAuthToken extends AbstractAuthenticationToken {
    private final String token;

    public WebAuthToken(String token) {
        super(null);
        this.token = token;
        setAuthenticated(false);
    }

    @Override
    public Object getCredentials() {
        return token;
    }

    @Override
    public Object getPrincipal() {
        return null;
    }
}

// 硬件认证专属Token
public class HardwareAuthToken extends AbstractAuthenticationToken {
    private final String token;

    public HardwareAuthToken(String token) {
        super(null);
        this.token = token;
        setAuthenticated(false);
    }

    @Override
    public Object getCredentials() {
        return token;
    }

    @Override
    public Object getPrincipal() {
        return null;
    }
}

2. 修改AuthenticationProvider的支持逻辑

让每个Provider仅处理对应类型的Token,杜绝交叉执行:

硬件认证Provider调整

@Component
public class HardwareAuthenticationProvider implements AuthenticationProvider {

    private final HardwareTokenService hardwareTokenService;

    // 构造注入依赖
    public HardwareAuthenticationProvider(HardwareTokenService hardwareTokenService) {
        this.hardwareTokenService = hardwareTokenService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        HardwareAuthToken authToken = (HardwareAuthToken) authentication;
        String vToken = authToken.getCredentials().toString();
        
        if (hardwareTokenService.auth(vToken)) {
            UserDetails userDetails = new User("hw", "", new ArrayList<>());
            // 返回已认证的Token
            return new UsernamePasswordAuthenticationToken(userDetails, vToken, userDetails.getAuthorities());
        } else {
            throw new BadCredentialsException("Invalid v-token");
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        // 仅支持HardwareAuthToken
        return HardwareAuthToken.class.isAssignableFrom(authentication);
    }
}

Web认证Provider调整

@RequiredArgsConstructor
@Component
public class TokenAuthenticationProvider implements AuthenticationProvider {

    private final AuthenticationService authenticationService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        WebAuthToken authToken = (WebAuthToken) authentication;
        String token = authToken.getCredentials().toString();
        
        UserDetails userDetails = Optional.ofNullable(token)
                .map(authenticationService::loadUserByToken)
                .orElseThrow(() -> new BadCredentialsException("Invalid authentication token=" + token));
        
        return new UsernamePasswordAuthenticationToken(userDetails, token, userDetails.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        // 仅支持WebAuthToken
        return WebAuthToken.class.isAssignableFrom(authentication);
    }
}

注:将原继承AbstractUserDetailsAuthenticationProvider改为直接实现AuthenticationProvider,逻辑更简洁,避免不必要的适配。

3. 调整ProcessingFilter的触发逻辑

每个Filter仅在请求头存在对应token时才启动认证流程,无对应头则直接跳过;同时检查SecurityContext,避免重复认证:

Web认证Filter

@Component
public class WebAuthenticationFilter extends OncePerRequestFilter {

    private final AuthenticationManager authenticationManager;

    public WebAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String webToken = request.getHeader("webAuth");
        // 仅当存在webAuth头且未认证时执行
        if (webToken != null && !webToken.isBlank() && SecurityContextHolder.getContext().getAuthentication() == null) {
            Authentication authToken = new WebAuthToken(webToken);
            Authentication authenticated = authenticationManager.authenticate(authToken);
            SecurityContextHolder.getContext().setAuthentication(authenticated);
        }
        filterChain.doFilter(request, response);
    }
}

硬件认证Filter

@Component
public class HardwareAuthenticationFilter extends OncePerRequestFilter {

    private final AuthenticationManager authenticationManager;

    public HardwareAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String hardwareToken = request.getHeader("hardwareAuth");
        // 仅当存在hardwareAuth头且未认证时执行
        if (hardwareToken != null && !hardwareToken.isBlank() && SecurityContextHolder.getContext().getAuthentication() == null) {
            Authentication authToken = new HardwareAuthToken(hardwareToken);
            Authentication authenticated = authenticationManager.authenticate(authToken);
            SecurityContextHolder.getContext().setAuthentication(authenticated);
        }
        filterChain.doFilter(request, response);
    }
}

4. 更新HttpSecurity配置

保持原有配置结构,无需额外调整,确保Provider和Filter正确注入即可:

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity.cors()
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .exceptionHandling()
            .defaultAuthenticationEntryPointFor(unauthorizedEntryPoint(), PROTECTED_URLS)
            .and()
            .authenticationProvider(webAuthenticationProvider)
            .authenticationProvider(hardwareAuthenticationProvider)
            .addFilterBefore(webAuthenticationFilter(), AnonymousAuthenticationFilter.class)
            .addFilterBefore(hardwareAuthenticationFilter(), AnonymousAuthenticationFilter.class)
            .authorizeRequests()
            .antMatchers(ANT_MATCHES).permitAll()
            .anyRequest().authenticated()
            .and()
            .csrf().disable()
            .formLogin().disable()
            .httpBasic().disable();
}

最终效果

  • 携带webAuth头的请求:仅Web认证流程执行,成功则通过,失败返回401。
  • 携带hardwareAuth头的请求:仅硬件认证流程执行,成功则通过,失败返回401。
  • 不会出现认证成功后后续Provider执行失败的情况,也不会出现第一个Provider失败后第二个不执行的问题。

内容的提问来源于stack exchange,提问作者Kaiak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 00:57:04