Spring Boot双安全配置(JWT+API Key)异常排查与正确配置
解决Spring Boot多安全策略配置问题:JWT与API Key共存
问题根源分析
- 第二个配置类
MySecurityConfiguration中的configure(WebSecurity web)将/api/users/**加入了忽略列表,导致该路径的请求完全绕过Spring Security过滤器链,包括你定义的APIKeyAuthFilter,这就是API Key请求未走对应过滤器、且无JWT也能直接访问的核心原因。 - 尽管第一个配置类
APISecurityConfig设置了@Order(1)和antMatcher("/api/users/**"),但WebSecurity的忽略规则优先级高于HttpSecurity配置,请求会直接跳过所有安全验证逻辑。
解决方案
- 移除
MySecurityConfiguration中对/api/users/**的忽略配置 - 确保两个配置类的
antMatcher明确划分路径范围,避免逻辑冲突 - 修正
APIKeyAuthFilter的逻辑:认证失败时直接返回401,防止请求流入后续过滤器链
修正后的代码
1. 主Security配置类
@Configuration @EnableWebSecurity public class SecurityConfig { @Configuration @Order(1) public static class APISecurityConfig extends WebSecurityConfigurerAdapter { @Value("${my.api.key.header}") private String principalRequestHeader; @Value("${my.api.key.token}") private String principalRequestValue; @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity .cors().disable().csrf().disable() // 明确仅处理/api/users/**路径 .antMatcher("/api/users/**") .authorizeRequests() .anyRequest().authenticated() .and() .addFilterBefore(new APIKeyAuthFilter(principalRequestHeader, principalRequestValue), UsernamePasswordAuthenticationFilter.class); } } @Order(2) @Configuration public static class MySecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired UserDetailsService userDetailsService; @Bean public AuthTokenFilter authenticationJwtTokenFilter() { return new AuthTokenFilter(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } // 仅保留真正无需认证的公开接口 @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/users/UserEmailExist", "/users/User/Add", "/users/Authenticate", "/users/User/ChangePassword"); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity .cors().disable().csrf().disable() // 明确处理/users/**路径 .antMatcher("/users/**") .authorizeRequests() .antMatchers("/users/UserEmailExist", "/users/User/Add", "/users/Authenticate", "/users/User/ChangePassword") .permitAll() .antMatchers("/users/**").hasAnyRole("ADMIN_USER", "MANAGER_USER") .anyRequest().authenticated() .and() .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); } } }
2. 修正后的APIKeyAuthFilter
public class APIKeyAuthFilter extends GenericFilterBean { private String principalRequestHeader; private String principalRequestValue; public APIKeyAuthFilter(String principalRequestHeader, String principalRequestValue) { super(); this.principalRequestHeader = principalRequestHeader; this.principalRequestValue = principalRequestValue; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; String apiKey = httpRequest.getHeader(principalRequestHeader); if (apiKey == null) { // 无API Key直接返回401 httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResponse.getWriter().write("API Key missing"); return; } if (!apiKey.equals(principalRequestValue)) { // API Key无效返回401 httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResponse.getWriter().write("Invalid API Key"); return; } // 认证通过,设置安全上下文 ApiKeyAuthenticationToken apiToken = new ApiKeyAuthenticationToken(apiKey, AuthorityUtils.NO_AUTHORITIES); SecurityContextHolder.getContext().setAuthentication(apiToken); chain.doFilter(request, response); } }
关键说明
- WebSecurity忽略规则:仅对完全不需要认证的公开接口设置忽略,不要包含需要API Key或JWT认证的路径。
- @Order优先级:
@Order(1)的配置类会优先处理请求,当请求匹配/api/users/**时,会由APISecurityConfig单独处理,不会流入后续的JWT配置逻辑。 - 过滤器逻辑优化:APIKeyAuthFilter在认证失败时直接终止请求,避免无效请求进入后续过滤器链,确保路径认证逻辑的单一性。
内容的提问来源于stack exchange,提问作者user2304483
相关产品推荐
相关产品推荐

