实现类

  • ApiKeyAuthFilter 认证过滤器
  • ApiKeyAuthenticationToken 认证令牌
  • ApiKeyAuthenticationProvider API认证鉴权
  • SecurityConfig 安全配置类

ApiKeyAuthFilter

方法概要
OncePerRequestFilter 是 Spring Web 提供的一个抽象过滤器基类,核心作用是确保过滤器的 doFilterInternal 方法在一次HTTP 请求的整个处理流程中只执行一次。
@Component 自动注册为 Spring Bean。
注入 AuthenticationManager

  • AuthenticationManager 是 Spring Security 的 认证总入口;
  • 调用它的 authenticate() 方法会:
    • 遍历所有 AuthenticationProvider
    • 找到支持 ApiKeyAuthenticationToken 的 Provider(即你的 ApiKeyAuthenticationProvider);
    • 执行验证逻辑。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      @Component
      public class ApiKeyAuthFilter extends OncePerRequestFilter {

      @Autowired
      private AuthenticationManager authenticationManager; // 注入 AuthenticationManager

      @Override
      protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {
      String apiKey = request.getHeader("X-API-Key");
      // 只处理 /api/ 开头的接口(根据你的需求调整)
      if (apiKey != null && request.getRequestURI().startsWith("/api/")) {
      try {

      // 1. 创建未认证的 Token
      ApiKeyAuthenticationToken authToken = new ApiKeyAuthenticationToken(apiKey);
      // 2. ⭐️ 关键:显式调用 AuthenticationManager 进行认证
      Authentication authenticated = authenticationManager.authenticate(authToken);
      // 3. 将认证成功的 Token 放入 SecurityContext
      SecurityContextHolder.getContext().setAuthentication(authenticated);

      } catch (AuthenticationException e) {
      // 认证失败:清空上下文,并返回 401
      SecurityContextHolder.clearContext();
      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      response.getWriter().write("Invalid API Key");
      return;
      }
      }

      filterChain.doFilter(request, response);
      }
      }

ApiKeyAuthenticationToken

AbstractAuthenticationToken 是 Spring Security 提供的认证令牌抽象类
继承实现用户登录以及令牌校验的操作,当 setAuthenticated 等于true时直接放行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ApiKeyAuthenticationToken extends AbstractAuthenticationToken {
private final Object principal;

public ApiKeyAuthenticationToken(Object principal) {
// credentials 设为 null(API Key 已用于认证,无需再存)
super(null);
this.principal = principal;
// 初始为未认证(由 AuthenticationProvider 设置为 true)
setAuthenticated(false);
}

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

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

ApiKeyAuthenticationProvider

Spring Security 中实现 API Key 认证的核心组件,它负责 验证客户端传入的 API Key 是否合法,并生成已认证的安全上下文(Authentication)。
核心关系:接口约定 + 方法实现(契约模式)
AuthenticationProviderSpring Security 定义的认证核心接口(契约),authenticate() 是这个接口强制要求实现的核心方法(契约内容)—— 简单说:

  • implements AuthenticationProvider:表示你的类「承诺遵守 Spring Security 的认证规则」;
  • authenticate():是你兑现这个承诺的「具体认证逻辑」(校验 API Key、用户名密码等)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    @Component
    public class ApiKeyAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private ISysApiKeysService sysApiKeysService;

    // 核心方法:执行具体的认证逻辑
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String providedKey = (String) authentication.getCredentials();
    SysApiKeys sysApiKeys = sysApiKeysService.selectByApikey(providedKey);
    if(sysApiKeys != null){
    ApiKeyAuthenticationToken authenticatedToken =
    new ApiKeyAuthenticationToken(sysApiKeys);
    return authenticatedToken;
    }

    throw new BadCredentialsException("Invalid API Key");
    }

    // 辅助方法:判断当前 Provider 是否支持处理某个类型的 Token
    @Override
    public boolean supports(Class<?> authentication) {
    return ApiKeyAuthenticationToken.class.isAssignableFrom(authentication);
    }
    }

SecurityConfig

@EnableMethodSecurity 注解(配合 @Configuration)是为了启用方法级别的权限控制,比如通过 @PreAuthorize@Secured 等注解限制接口访问
addFilterBefore()Spring Security 中用于自定义过滤器链顺序的核心方法,它的作用是:
在指定的内置(或已注册)Filter 之前插入你自己的 Filter。所以必须在 原有UsernamePasswordAuthenticationFilter 认证之前去先走apiKeyAuthFilter

1
2
// 添加 Api filter
.addFilterBefore(apiKeyAuthFilter,UsernamePasswordAuthenticationFilter.class)

protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception
Spring Security 中配置 HTTP 层面安全规则的核心方法,它的核心作用是通过 HttpSecurity 对象定制请求的安全策略(如哪些请求需要认证、哪些放行、用什么方式认证、异常如何处理等)。下面我会从「核心作用、常用配置、实战示例、与方法级权限的区别」四个维度讲清楚这个方法的用处。

  • 这个方法的本质是构建一个 SecurityFilterChain 过滤器链,Spring Security 会将这个过滤器链应用到所有
  • HTTP 请求上,实现: 控制哪些请求需要认证、哪些请求可以匿名访问(放行);
  • 定义认证方式(如 HTTP Basic、表单登录、JWT
    过滤器等);
  • 配置跨域(CORS)、CSRF 防护、会话管理; 定制认证失败、权限不足的响应(如返回 JSON 而非默认页面);
  • 整合自定义过滤器(如 JWT 校验过滤器)。

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
* spring security配置
*
* @author
*/
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Configuration
public class SecurityConfig
{
/**
* 自定义用户认证逻辑
*/
@Autowired
private UserDetailsService userDetailsService;

/**
* 认证失败处理类
*/
@Autowired
private AuthenticationEntryPointImpl unauthorizedHandler;

/**
* 退出处理类
*/
@Autowired
private LogoutSuccessHandlerImpl logoutSuccessHandler;

/**
* token认证过滤器
*/
@Autowired
private JwtAuthenticationTokenFilter authenticationTokenFilter;

@Autowired
private ApiKeyAuthFilter apiKeyAuthFilter;

/**
* 跨域过滤器
*/
@Autowired
private CorsFilter corsFilter;

/**
* 允许匿名访问的地址
*/
@Autowired
private PermitAllUrlProperties permitAllUrl;

@Autowired
ApiKeyAuthenticationProvider apiKeyAuthenticationProvider;

/**
* 身份验证实现
*/
@Bean
public AuthenticationManager authenticationManager()
{
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());

return new ProviderManager(Arrays.asList(daoAuthenticationProvider,
apiKeyAuthenticationProvider));
}

/**
* anyRequest | 匹配所有请求路径
* access | SpringEl表达式结果为true时可以访问
* anonymous | 匿名可以访问
* denyAll | 用户不能访问
* fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)
* hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问
* hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问
* hasAuthority | 如果有参数,参数表示权限,则其权限可以访问
* hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
* hasRole | 如果有参数,参数表示角色,则其角色可以访问
* permitAll | 用户可以任意访问
* rememberMe | 允许通过remember-me登录的用户访问
* authenticated | 用户登录后可访问
*/
@Bean
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception
{
return httpSecurity
// CSRF禁用,因为不使用session
.csrf(csrf -> csrf.disable())
// 禁用HTTP响应标头
.headers((headersCustomizer) -> {
headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin());
})
// 认证失败处理类
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
// 基于token,所以不需要session
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// 注解标记允许匿名访问的url
.authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET,
"/",
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/profile/**",
"/cadre/neo4j/**",
"/cadre/evaluation/rqcode/**")
.permitAll()
.antMatchers(HttpMethod.POST,
"/cadre/cadre/home/recalculate")
.permitAll()
.antMatchers(
"/swagger-ui.html",
"/swagger-resources/**",
"/webjars/**",
"/*/api-docs",
"/cadre/evaluation/qrcode/**",
"/cadre/evaluation/grade/rqcodeScore/**",
"/druid/**")
.permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
})
// 添加Logout filter
.logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler))
// 添加 Api filter
.addFilterBefore(apiKeyAuthFilter, UsernamePasswordAuthenticationFilter.class)
// 添加JWT filter
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
// 添加CORS filter
.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
.addFilterBefore(corsFilter, LogoutFilter.class)
.build();
}

/**
* 强散列哈希加密实现
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder()
{
return new BCryptPasswordEncoder();
}
}