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

SpringBoot3.3+SpringSecurity6自定义过滤器与认证提供者适配问题

Spring Security 6 自定义过滤器与认证提供者配置解决方案

问题根源

  1. 认证提供者未正确关联:旧版configureGlobal方法依赖WebSecurityConfigurerAdapter,在Spring Security 6中该配置方式已失效,导致自定义AuthenticationProvider未被纳入当前安全过滤器链。
  2. 过滤器逻辑跳过认证流程:原自定义过滤器直接将认证对象存入SecurityContext,Spring Security会判定请求已完成认证,不会触发AuthenticationProvider的校验逻辑。

修正步骤

1. 调整自定义过滤器逻辑

改用OncePerRequestFilter(确保单次请求仅执行一次),逻辑改为提取认证凭证→创建未认证对象→交由AuthenticationManager触发认证→认证通过后存入SecurityContext:

@Component
public class CustomFilter extends OncePerRequestFilter {
    private final AuthenticationManager authenticationManager;

    // 构造注入AuthenticationManager
    public CustomFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 从请求中提取认证信息(示例从请求头提取,可根据实际场景调整)
        String username = request.getHeader("X-Username");
        String password = request.getHeader("X-Password");

        if (username != null && password != null) {
            // 创建未认证的Authentication对象
            Authentication unauthenticatedAuth = new UsernamePasswordAuthenticationToken(username, password);
            // 交由AuthenticationManager认证,自动触发自定义AuthenticationProvider的authenticate方法
            Authentication authenticatedAuth = authenticationManager.authenticate(unauthenticatedAuth);
            // 认证通过后存入SecurityContext
            SecurityContextHolder.getContext().setAuthentication(authenticatedAuth);
        }
        filterChain.doFilter(request, response);
    }
}

2. 修正SecurityConfiguration配置

在SecurityFilterChain中直接关联AuthenticationProvider,并通过Spring注入自定义过滤器(避免手动new导致依赖缺失):

@Slf4j
@Configuration
@EnableWebSecurity
// 启用方法级安全(替代旧版@EnableGlobalMethodSecurity)
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfiguration {
    private final CustomAuthenticationProvider customAuthenticationProvider;
    private final CustomFilter customFilter;

    // 构造注入依赖
    public SecurityConfiguration(CustomAuthenticationProvider customAuthenticationProvider, CustomFilter customFilter) {
        this.customAuthenticationProvider = customAuthenticationProvider;
        this.customFilter = customFilter;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/secure/**", "/api/**").authenticated() // 配置需要认证的端点
                        .anyRequest().permitAll()
                )
                // 注册自定义认证提供者
                .authenticationProvider(customAuthenticationProvider)
                // 添加自定义过滤器,放在BasicAuthenticationFilter之前
                .addFilterBefore(customFilter, BasicAuthenticationFilter.class)
                .build();
    }

    // 注册AuthenticationManager Bean,自动关联已配置的AuthenticationProvider
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }
}

3. 确保AuthenticationProvider逻辑正确

完善authenticate方法的返回逻辑,并指定支持的Authentication类型:

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
    private final UserRepository userRepository;

    public CustomAuthenticationProvider(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        // 查询用户信息
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }

        // 校验密码
        if (!isAuthenticated(user, password)) {
            throw new BadCredentialsException("密码错误");
        }

        // 返回已认证的Authentication对象,需包含用户权限信息
        return new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
    }

    // 指定该提供者支持的Authentication类型
    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }

    private boolean isAuthenticated(User user, String password) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder.matches(password, user.getPassword());
    }
}

关键注意事项

  • 避免手动new自定义过滤器实例,必须通过Spring注入,确保依赖(如AuthenticationManager)正确初始化。
  • 过滤器仅负责提取凭证并触发认证,具体校验逻辑交由AuthenticationProvider处理,遵循单一职责原则。
  • 若无需方法级安全,可移除@EnableMethodSecurity注解。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 07:32:19