新线程中获取Spring Security认证信息

在做一些耗时的操作逻辑时,经常会通过开启新线程进行处理。但是,直接通过new Thread()/new Runnable() 方式创建的线程中没有用户的认证信息(即SecurityContextHolder.getContext().getAuthentication() 返回的是 null)。这是由于 认证信息 是在请求到达后台,经由 Security 的一系列过滤器后将 认证信息写入到 线程本地变量中。所以,新开的线程并没有执行过滤器的逻辑,自然没有认证信息。
既然不会自动设置认证信息,那我们只有手动设置认证信息了。

方式一: 手动设置线程中的认证信息

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

// 新线程
Runnable runnable = new Runnable() {
	public void run() {
		// 手动设置线程中的认证信息
		SecurityContextHolder.getContext().setAuthentication(authentication);
		
		// 线程处理逻辑(后续就能获取到认证信息)
		...
	}
};
new Thread(runnable).start();

方式二:使用 DelegatingSecurityContextRunnable 创建线程

官方文档:https://docs.spring.io/spring-security/site/docs/5.0.16.RELEASE/reference/htmlsingle/#delegatingsecuritycontextrunnable

Spring Security 考虑到了新线程需要访问认证信息的情况,提供了 DelegatingSecurityContextRunnable 类,通过该类构建新线程(Runnable),线程内部自然能获取认证信息。
查看DelegatingSecurityContextRunnable的源码, 你会发现,本质上思路和方式一是一致的。

Runnable runnable = new DelegatingSecurityContextRunnable(() -> {
  // 线程处理逻辑
  ...
});
new Thread(runnable).start();

或者

Runnable runnable = DelegatingSecurityContextRunnable.create(() -> {
  //线程处理逻辑
  ...
}, SecurityContextHolder.getContext());
new Thread(runnable).start();

另:
Spring Security 提供一个线程池DelegatingSecurityContextExecutor,线程池类所有线程公用一个认证信息。如果是执行比较通用的逻辑,并不涉及个人的认证信息。其应该是一个比较好的方案。

官方文档:https://docs.spring.io/spring-security/site/docs/5.0.16.RELEASE/reference/htmlsingle/#delegatingsecuritycontextexecutor

reference:
官方文档:Spring Security 对并发的支持


end

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章