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

迁移至SecurityFilterChain后AuthenticationFilter失效问题求助

问题解决方案

核心问题分析

  1. 授权过滤器拦截未认证请求:AuthorizationFilter被全局拦截所有请求,登录接口未携带Token时直接抛出异常,返回401。
  2. Security规则配置分散:两次调用authorizeRequests存在规则匹配顺序风险,且未明确剩余路径的认证要求。
  3. 认证管理器未绑定必要组件:未显式配置UserDetailsService和密码编码器,导致AuthenticationManager无法正确执行用户认证逻辑。

修改步骤及代码

1. 修正SecurityConfiguration配置

合并规则配置,排除公开路径的授权拦截,绑定UserDetailsService和密码编码器:

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
class SecurityConfiguration {
    private final UserDetailsService userDetailsService;
    private final SuffixConfiguration suffixConfiguration;
    private final AuthorizationService authorizationService;
    private final AuthenticationService authenticationService;
    private static final String LOGIN_URL = "/api/users/login";
    // 定义所有公开路径
    private static final String[] PUBLIC_URLS = {
        LOGIN_URL,
        "/api/users/register",
        "/api/users/refreshToken"
    };

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        AuthenticationManager authenticationManager = authenticationManager(http.getSharedObject(AuthenticationConfiguration.class));
        
        AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager, authenticationService);
        authenticationFilter.setFilterProcessesUrl(LOGIN_URL);
        AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationService);

        http
                .headers().cacheControl().and()
                .csrf().disable()
                .cors().and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                // 公开路径允许匿名访问
                .antMatchers(PUBLIC_URLS).permitAll()
                // 管理员专属路径权限校验
                .antMatchers("/api/department/add-moderator").hasAuthority("ROLE_ADMIN")
                // 剩余所有路径需认证
                .anyRequest().authenticated().and()
                .addFilter(authenticationFilter)
                .addFilterBefore(authorizationFilter, UsernamePasswordAuthenticationFilter.class)
                // 绑定UserDetailsService和密码编码器
                .userDetailsService(userDetailsService)
                .passwordEncoder(suffixConfiguration.bCryptPasswordEncoder());

        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }
}

2. 优化AuthorizationServiceImpl逻辑

添加未携带Token的请求放行逻辑,优化异常处理:

@Slf4j
@Service
public class AuthorizationServiceImpl implements AuthorizationService {
    private static final String AUTHORIZATION = "Authorization";
    private static final String TOKEN_PREFIX = "Bearer ";
    private static final String SECRET = "your-secret-key"; // 替换为实际密钥

    @Override
    public void tryAuthorize(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String authHeader = request.getHeader(AUTHORIZATION);
        // 无Token或非Bearer格式,直接放行给后续过滤器处理
        if (authHeader == null || !authHeader.startsWith(TOKEN_PREFIX)) {
            filterChain.doFilter(request, response);
            return;
        }
        
        String token = authHeader.substring(TOKEN_PREFIX.length());
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET.getBytes());
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT decodedJWT = verifier.verify(token);
            String username = decodedJWT.getSubject();
            String[] roles = decodedJWT.getClaim("roles").asArray(String.class);
            Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
            Arrays.stream(roles).forEach(role -> authorities.add(new SimpleGrantedAuthority(role)));
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities);
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            filterChain.doFilter(request, response);
        } catch (Exception exception) {
            log.error("认证异常: {}", exception.getMessage());
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            // 返回自定义错误响应
            response.setContentType("application/json");
            response.getWriter().write("{\"error\":\"无效的访问令牌\"}");
            response.getWriter().flush();
        }
    }
}

3. 确保AuthenticationFilter正确实现

如果AuthenticationFilter继承自AbstractAuthenticationProcessingFilter,需确保重写关键方法:

public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    private final AuthenticationService authenticationService;

    public AuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationService authenticationService) {
        super(new AntPathRequestMatcher("/api/users/login", "POST"));
        setAuthenticationManager(authenticationManager);
        this.authenticationService = authenticationService;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException {
        // 解析请求参数生成认证令牌
        UsernamePasswordAuthenticationToken authRequest = authenticationService.createUsernameAuthenticationToken(request, response);
        // 交给AuthenticationManager执行认证
        return getAuthenticationManager().authenticate(authRequest);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException {
        // 生成认证成功响应并返回
        Map<Object, Object> responseBody = authenticationService.successfulAuthentication(request, response, authResult);
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_OK);
        new ObjectMapper().writeValue(response.getWriter(), responseBody);
    }
}

关键说明

  • 公开路径(登录、注册、刷新Token)不再被AuthorizationFilter拦截,直接放行给认证逻辑处理。
  • 显式绑定UserDetailsService和密码编码器,确保AuthenticationManager能正确查询用户信息并验证密码。
  • 优化异常处理,避免未携带Token的请求直接抛出异常,改为返回标准401错误响应。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 02:45:33