spring boot Security 获取上下文用户信息的问题总结
@RequestMapping("/hello")
public String hello() {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println( "用户名:" + authentication.getName() );
new Thread(() -> {
// 先复制父线程上下文
SecurityContextHolder.setContext(context);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println( "子线程用户名2:" + auth );
}).start();
return "Hello World!";
}
如何把 获取用户信息做成一个公共的方法使其 可以在子线程 能方便的获取 登录用户信息
现全局可用的安全上下文传递,使其在子线程中也能像主线程一样访问,可以通过以下几种方式实现。我将从简单到复杂给出解决方案
方案1:静态工具类(最简单)
// 创建安全上下文工具类
public class SecurityContextUtils {
private static final InheritableThreadLocal<SecurityContext> contextHolder =
new InheritableThreadLocal<>();
public static void setContext(SecurityContext context) {
contextHolder.set(context);
}
public static SecurityContext getContext() {
return contextHolder.get();
}
public static void clear() {
contextHolder.remove();
}
}
// 使用方式(在需要传递上下文的地方):
SecurityContext context = SecurityContextHolder.getContext();
SecurityContextUtils.setContext(context);
new Thread(() -> {
SecurityContext ctx = SecurityContextUtils.getContext();
Authentication auth = ctx.getAuthentication();
System.out.println("子线程用户: " + auth.getName());
}).start();
方案2:Spring Bean + AOP(推荐生产环境使用)
@Component
public class SecurityContextPropagator {
public void executeInContext(Runnable task) {
SecurityContext context = SecurityContextHolder.getContext();
new Thread(() -> {
try {
SecurityContextHolder.setContext(context);
task.run();
} finally {
SecurityContextHolder.clearContext();
}
}).start();
}
}
// 使用方式:
@Autowired
private SecurityContextPropagator propagator;
propagator.executeInContext(() -> {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println("子线程用户: " + auth.getName());
});
方案3:自定义线程池(最完整方案)
@Configuration
public class ThreadPoolConfig {
@Bean
public ExecutorService securityContextAwareExecutor() {
return new ContextCopyingExecutorService(
Executors.newCachedThreadPool()
);
}
static class ContextCopyingExecutorService extends ThreadPoolExecutor {
// 实现所有构造方法...
@Override
public void execute(Runnable command) {
SecurityContext context = SecurityContextHolder.getContext();
super.execute(() -> {
try {
SecurityContextHolder.setContext(context);
command.run();
} finally {
SecurityContextHolder.clearContext();
}
});
}
}
}
// 使用方式:
@Autowired
private ExecutorService securityContextAwareExecutor;
securityContextAwareExecutor.execute(() -> {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println("线程池用户: " + auth.getName());
});
方案4:Spring Security 5.3+ 的官方方案
// 配置类
@Configuration
public class SecurityConfig {
@Bean
public SecurityContextHolderStrategy securityContextHolderStrategy() {
return new InheritableThreadLocalSecurityContextHolderStrategy();
}
}
// 使用方式(无需修改原有代码):
new Thread(() -> {
// 现在能自动获取父线程上下文
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
}).start();
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 静态工具类 | 简单直接 | 需要手动调用 | 小型应用 |
| Spring Bean | 可注入管理 | 需要依赖Spring | 常规Spring项目 |
| 自定义线程池 | 完整控制 | 实现复杂 | 需要线程池的场景 |
| Spring官方方案 | 最规范 | 需要5.3+版本 | 新项目 |
如果是新项目:直接使用方案4(Spring Security 5.3+)
传统Spring项目:选择方案2(Spring Bean)
需要精细控制线程:用方案3(自定义线程池)
快速原型开发:用方案1(静态工具类)
(。・v・。)
喜欢这篇文章吗?欢迎分享到你的微博、QQ群,并关注我们的微博,谢谢支持。