本文内容来自王松老师的《深入浅出Spring Security》,自己在学习的时候为了加深理解顺手抄录的,有时候还会写一些自己的想法。
如果不使用Spring Security这一类的安全框架,大部分开发者可能将会把登录信息保存在Session中。事实上Spring Security也是这么做的。但是为了方便,Spring Security在此基础上还做了一些改进,其中主要的一个变化就是线程绑定(一提到线程绑定,敏感的小伙伴就应该会想到ThreadLocal)。
当用户登录成功后,Spring Security会将登录成功的用户信息保存在SecurityContextHolder中。SecurityContextHolder中保存的数据默认是通过ThreadLocal来实现的,使用ThreadLocal创建的变量只能被当前线程访问,不能被其他线程访问和修改,也就是用户数据和当前请求线程绑定在一起。当登录请求处理完毕后,Spring Security会将保存在SecurityContextHolder中的熟路拿出来保存到Session中,同时将SpringContextHolder中的数清空。以后每次请求到来时,Spring Security就会先从Session中取出数据保存到SecurityContextHolder中,方便该请求在后续处理中使用。在请求处理结束时将SecurityContextHolder中的数据拿出来放入Session中,然后清空SecurityContextHolder中的数据。
这一策略非常方便用户在Controller或者Service层中获取当前登录用的数据,但是带来另外一个问题就是在子线程中如果想要获取当前登录用户的信息就比较麻烦。Spring Security也提供了对应的解决方案,如果开发者使用@Async注解来开启一步任务的话,那么只需要添加如下配置使用Spring Security提供的异步任务代理,就可以在异步任务中的SecurityContextHolder获取当前登录用户的信息了:
@Configuration
public class ApplicationConfiguration extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
return new DelegatingSecurityContextExecutorService(Executors.newFixedThreadPool(5));
}
}