Spring Boot多认证配置异常:API Key端点报SecurityContext无认证对象
问题原因及修复方案
出现"An Authentication object was not found in the SecurityContext"错误的核心原因:
- 第一个
SecurityFilterChain(API Key专用)缺少授权规则配置,Spring Security无法识别你通过过滤器设置的认证对象 - 使用普通
Filter而非Spring Security推荐的OncePerRequestFilter,可能导致过滤器执行逻辑不符合Security链的预期
具体修复步骤
1. 完善API Key专用的SecurityFilterChain配置
在filterChainPrivate方法中添加授权规则和无状态会话配置:
@Bean @Order(1) public SecurityFilterChain filterChainPrivate(HttpSecurity http) throws Exception { http .securityMatcher("/specificEndpointToAuthoriseWithApiKey") // 明确所有匹配的请求需要认证 .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) // 配置无状态会话(API Key认证不需要会话) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .addFilterBefore(new InternalApiKeyAuthenticationFilter(), ChannelProcessingFilter.class); http.cors(AbstractHttpConfigurer::disable); http.csrf(AbstractHttpConfigurer::disable); http.exceptionHandling(httpSecurityExceptionHandlingConfigurer -> { httpSecurityExceptionHandlingConfigurer.accessDeniedHandler(getAccessDeniedHandler()); httpSecurityExceptionHandlingConfigurer.authenticationEntryPoint(getAuthenticationEntryPoint()); }); return http.build(); }
2. 将API Key过滤器改为继承OncePerRequestFilter
替换普通Filter实现为Spring Security专用的OncePerRequestFilter,确保每个请求仅执行一次认证逻辑:
import org.springframework.web.filter.OncePerRequestFilter; public class InternalApiKeyAuthenticationFilter extends OncePerRequestFilter { private final String internalApiKey = "123456"; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String apiKey = request.getHeader("x-api-key"); if (apiKey == null) { unauthorized(response); return; } if (!internalApiKey.equals(apiKey)) { unauthorized(response); return; } UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( "apiKeyUser", null, Collections.singletonList(new SimpleGrantedAuthority("ebf-adapter"))); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); filterChain.doFilter(request, response); } private void unauthorized(HttpServletResponse httpServletResponse) throws IOException { httpServletResponse.setHeader(com.google.common.net.HttpHeaders.CONTENT_TYPE, org.springframework.http.MediaType.APPLICATION_JSON_VALUE); httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); Map<String, Object> response = Map.of("message", "SC_UNAUTHORIZED"); String responseBody = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(response); httpServletResponse.getWriter().write(responseBody); } }
3. (可选)将过滤器交由Spring容器管理
在SecurityConfiguration中添加过滤器的Bean定义,避免手动实例化:
@Bean public InternalApiKeyAuthenticationFilter internalApiKeyAuthenticationFilter() { return new InternalApiKeyAuthenticationFilter(); }
然后修改filterChainPrivate中的过滤器引用:
.addFilterBefore(internalApiKeyAuthenticationFilter(), ChannelProcessingFilter.class);
4. 修正第二个SecurityFilterChain的语法错误
你的第二个FilterChain代码存在括号未闭合的问题,同时补充JWT资源服务器配置:
@Bean @Order(2) SecurityFilterChain filterChain(HttpSecurity http, Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter) throws Exception { http.authorizeHttpRequests(accessManagement -> accessManagement .requestMatchers(new AntPathRequestMatcher("/api-docs/**")) .permitAll() .anyRequest().authenticated() ); // 配置JWT资源服务器 http.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter))); return http.build(); }
内容的提问来源于stack exchange,提问作者aermolov
相关产品推荐
相关产品推荐

