自定义认证过滤器认证成功仍返回403,如何适配Spring Security?
问题:自定义认证过滤器通过认证后仍返回403,如何让Spring Security识别认证状态?
我的SettingsAPIAuthFilter继承了OncePerRequestFilter,已经能成功完成第三方认证,但因为配置了.authorizeHttpRequests(auth -> auth.anyRequest().authenticated()),请求还是返回403。想问下该怎么调整,才能让Spring Security的认证校验识别到请求已经通过认证?
我的SecurityConfig代码
@Configuration @EnableWebSecurity public class SecurityConfig { @Autowired private SettingsAPIAuthFilter settingsAPIAuthFilter; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { return httpSecurity .formLogin(AbstractHttpConfigurer::disable) .httpBasic(AbstractHttpConfigurer::disable) .addFilterBefore(settingsAPIAuthFilter, UsernamePasswordAuthenticationFilter.class) .cors(corsConfigurer -> corsConfigurer.configurationSource(request -> { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowedOrigins(List.of("*")); corsConfiguration.setAllowedMethods(List.of(HttpMethod.GET.name(), "POST", "PUT", "DELETE")); corsConfiguration.setAllowedHeaders(List.of("*")); return corsConfiguration; })) .authorizeHttpRequests(auth -> auth.anyRequest() .authenticated()) .build(); } }
我的SettingsAPIAuthFilter代码
@Component public class SettingsAPIAuthFilter extends OncePerRequestFilter { private static final String API_AUTHORISE_REQ_ACTION_ID_QUERY_PARAM = "requestActionId"; private static final String API_AUTH_REQ_SESSION_ID_HEADER_NAME = "userSessionId"; public static final String MAIN_API_AUTHORISE_ENDPOINT = "/authorise"; private final AppEventHandler appEventHandler = new AppEventHandler(); private final WebClient mainApiWebClient; public SettingsAPIAuthFilter( @Qualifier("mainApiWebClient") WebClient webClient ) { this.mainApiWebClient = webClient; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { ResponseEntity authResponse = authoriseRequest( request.getHeader("userSessionId"), "123"); if (authResponse.getStatusCode().value() == 200) { appEventHandler.info("Authorisation successful. Moving on..."); filterChain.doFilter(request, response); } response.setStatus(authResponse.getStatusCode().value()); response.setHeader("Content-Type", "application/json"); response.getOutputStream().print((String) authResponse.getBody()); } catch (Exception exception) { appEventHandler.error("Error occurred in SettingsAPIAuthFilter", exception); } } public ResponseEntity<?> authoriseRequest( String sessionId, String requestedActionId ) throws Exception { try { return mainApiWebClient.get() .uri(uriBuilder -> uriBuilder .path(MAIN_API_AUTHORISE_ENDPOINT) .queryParam(API_AUTHORISE_REQ_ACTION_ID_QUERY_PARAM, requestedActionId) .build() ) .header(API_AUTH_REQ_SESSION_ID_HEADER_NAME, sessionId) .retrieve() .onStatus(HttpStatusCode::isError, errorResponse -> Mono.empty()) .toEntity(String.class) .block(); } catch (Exception exception) { appEventHandler.error("Exception occurred in authorising request : ", exception); throw new Exception("Exception occurred in authorising request : ", exception); } } }
解决办法
问题根源
你的过滤器虽然完成了第三方认证,但没把认证信息存入Spring Security的SecurityContext,后续的anyRequest().authenticated()校验根本不知道请求已经通过认证,所以直接返回403。
不需要更换过滤器父类,只需补充这一步
继承OncePerRequestFilter完全没问题,不用换其他父类。你要做的就是在认证成功后,把用户的认证信息封装成Authentication对象,存入SecurityContextHolder。
修改SettingsAPIAuthFilter的doFilterInternal方法:
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { ResponseEntity authResponse = authoriseRequest( request.getHeader("userSessionId"), "123"); if (authResponse.getStatusCode().value() == 200) { appEventHandler.info("Authorisation successful. Moving on..."); // 关键步骤:构造认证对象并放入安全上下文 // 这里可以根据第三方返回的实际内容,替换成真实的用户信息和权限 Authentication auth = new UsernamePasswordAuthenticationToken( "用户唯一标识", // 比如从authResponse解析出的用户ID/username null, // 凭证信息,不需要的话设为null Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")) // 用户权限,按需调整 ); SecurityContextHolder.getContext().setAuthentication(auth); // 放行请求 filterChain.doFilter(request, response); // 清理上下文,避免线程池复用导致的上下文污染 SecurityContextHolder.clearContext(); return; // 直接返回,不要执行后面的响应设置 } response.setStatus(authResponse.getStatusCode().value()); response.setHeader("Content-Type", "application/json"); response.getOutputStream().print((String) authResponse.getBody()); } catch (Exception exception) { appEventHandler.error("Error occurred in SettingsAPIAuthFilter", exception); // 异常时返回500错误 response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.getOutputStream().print("{\"error\":\"认证过程出现异常\"}"); } }
额外注意点
- 权限配置:如果你的系统需要权限控制,要从第三方认证接口返回的内容里解析出用户的实际权限,替换示例中的
ROLE_USER。 - 线程安全:
SecurityContextHolder默认用ThreadLocal存上下文,所以在请求处理完后一定要调用clearContext(),防止线程池复用上下文导致的问题。 - 异常处理:原代码里异常时没设置响应状态,建议补充,避免请求无响应或返回默认错误。
内容的提问来源于stack exchange,提问作者Kalindu
相关产品推荐
相关产品推荐

