1. 概述

Spring Security的Java配置支持提供了强大的流式API,用于定义应用程序的安全映射和规则。本文将深入探讨如何更进一步——定义自定义配置器:这是一种高级且灵活的方式,能将自定义逻辑融入标准安全配置中。

我们通过一个简单场景实现这个功能:根据指定的错误状态码列表,为已认证用户记录错误日志。

2. 自定义SecurityConfigurer

要定义自定义配置器,首先需要继承AbstractHttpConfigurer

public class ClientErrorLoggingConfigurer 
  extends AbstractHttpConfigurer<ClientErrorLoggingConfigurer, HttpSecurity> {

    private List<HttpStatus> errorCodes;
    
    // 标准构造方法
    
    @Override
    public void init(HttpSecurity http) throws Exception {
        // 初始化代码
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
       http.addFilterAfter(
         new ClientErrorLoggingFilter(errorCodes), 
         FilterSecurityInterceptor.class);
    }
}

**核心方法是configure()**——包含该配置器要应用的安全逻辑。在我们的示例中:

  • 在Spring Security最后一个过滤器后注册新过滤器
  • 添加errorCodes属性控制需记录的错误码

init()方法(可选)在configure()前执行,可用于额外配置。

接下来定义过滤器类:

public class ClientErrorLoggingFilter extends GenericFilterBean {

    private static final Logger logger = LogManager.getLogger(
      ClientErrorLoggingFilter.class);
    private List<HttpStatus> errorCodes;

    // 标准构造方法

    @Override
    public void doFilter(
      ServletRequest request, 
      ServletResponse response, 
      FilterChain chain) 
      throws IOException, ServletException {
        //...

        chain.doFilter(request, response);
    }
}

这是标准Spring过滤器,继承GenericFilterBean并重写doFilter()。重点关注其核心逻辑:

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth == null) {
    chain.doFilter(request, response);
    return;
}
int status = ((HttpServletResponse) response).getStatus();
if (status < 400 || status >= 500) {
    chain.doFilter(request, response);
    return;
}
if (errorCodes == null) {
    logger.debug("User " + auth.getName() + " encountered error " + status);
} else {
    if (errorCodes.stream().anyMatch(s -> s.value() == status)) {
        logger.debug("User " + auth.getName() + " encountered error " + status);
    }
}

执行流程:

  1. ✅ 获取当前认证信息,未认证则跳过
  2. ✅ 检查响应状态码(仅处理400-499客户端错误)
  3. ✅ 根据配置决定是否记录日志:
    • 未指定错误码时记录所有客户端错误
    • 指定错误码时仅记录匹配的错误

3. 使用自定义配置器

通过两步集成自定义配置器:定义Bean + 使用apply()方法

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            //...
            .and()
            .apply(clientErrorLogging());
        return http.build();
    }

    @Bean
    public ClientErrorLoggingConfigurer clientErrorLogging() {
        return new ClientErrorLoggingConfigurer();
    }

}

也可指定特定错误码:

@Bean
public ClientErrorLoggingConfigurer clientErrorLogging() {
    return new ClientErrorLoggingConfigurer(Arrays.asList(HttpStatus.NOT_FOUND)) ;
}

⚠️ 踩坑提示:确保apply()调用在安全链配置的末尾,避免被其他配置覆盖。

高级用法

默认启用配置器:在META-INF/spring.factories中声明:

org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = com.baeldung.dsl.ClientErrorLoggingConfigurer

手动禁用配置器

//...
.apply(clientErrorLogging()).disable();

4. 总结

本文展示了Spring Security的高级特性——**如何创建自定义SecurityConfigurer**。这种模式特别适合:

  • 封装可复用的安全逻辑
  • 实现复杂的安全扩展
  • 保持配置代码的整洁性

完整示例代码请查看GitHub仓库


原始标题:A Custom Spring SecurityConfigurer