问题背景:部分接口调用正常,部分接口调用报403Forbidden,postman不显示具体报错信息。
问题描述:
接口调用报错,经排查,权限校验认证通过,可以进入接口,但是在执行过程中,安全上下文丢失,导致返回403异常。相关日志:
2024-10-24 21:07:58.096 [http-nio-8029-exec-6] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Set SecurityContextHolder to anonymous SecurityContext
2024-10-24 21:07:58.097 [http-nio-8029-exec-6] DEBUG o.s.s.w.s.HttpSessionRequestCache - Saved request http://xxx:8029/error?continue to session
2024-10-24 21:07:58.097 [http-nio-8029-exec-6] DEBUG o.s.s.w.a.Http403ForbiddenEntryPoint - Pre-authenticated entry point called. Rejecting access
问题出现原因与解决方式:
解决方式
securityConfig中添加以下几行代码就能解决:
@Bean
public SecurityContextRepository securityContextRepository() {return new HttpSessionSecurityContextRepository();
}
出现原因:
方法中使用了CompletableFuture异步方法,异步执行的线程无法获取到原线程的安全上下文,导致权限校验失败。
可能出现这个情况的原因:
- 安全上下文的持久化:
HttpSessionSecurityContextRepository 负责将 SecurityContext 存储在 HTTP 会话中。这意味着每个用户的安全上下文在会话期间是持久化的。
当请求到达时,Spring Security 会从会话中恢复 SecurityContext,确保用户的认证信息在整个会话期间保持一致。 - 异步请求的安全上下文:
在异步操作中,默认情况下,SecurityContext 可能不会自动传播到新线程中。
使用 HttpSessionSecurityContextRepository 可以确保在异步请求中,SecurityContext 能够从会话中正确恢复。 - 403 错误的原因:
之前的 403 错误可能是因为在异步操作中,SecurityContext 丢失或未正确设置,导致请求被视为匿名用户。
通过将 SecurityContext 存储在会话中,Spring Security 能够在每个请求中恢复用户的认证信息,即使是在异步操作中。
以上结合AI和个人理解给出原因分析,若有不同意见请指正。
问题排查过程:
以下方案都试过了都没用,如果其他人遇到可以试试
-
使用 DelegatingSecurityContextAsyncTaskExecutor:(Spring 提供了DelegatingSecurityContextAsyncTaskExecutor,可以在执行异步任务时传递安全上下文。)并在调用方法使用 @Async 注解。
-
手动传递 SecurityContext:
public CompletableFuture<RpcResponse<String>> yourMethod() {SecurityContext context = SecurityContextHolder.getContext();return CompletableFuture.supplyAsync(() -> {SecurityContextHolder.setContext(context);try {// 异步执行的代码return rpcService.call();} finally {SecurityContextHolder.clearContext();}});
}
- 配置允许匿名访问错误页面:
@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/error").permitAll()// 其他路径的权限配置.anyRequest().authenticated().and()// 其他配置
}