在Spring Security中,SecurityContextHolder 是一个中心存储点,用于存储有关当前认证用户的信息。这些信息通常包括用户名、角色、权限等。当应用程序处理安全相关的操作时,例如验证用户身份或检查用户权限,这些信息都是非常关键的。SecurityContextHolder 支持多种存储策略,这些策略决定了安全上下文如何在不同的线程之间传递。
MODE_THREADLOCAL工作模式,默认情况下,SecurityContextHolder使用ThreadLocal机制来保存上下文信息,也就是说,只要使用者的逻辑执行都在一个线程中使用的话,其就可以在任何地方获得上下文信息。此方式也是非常适合Servlet web应用,因为对于一个请求的处理,不管经历了多少Filter,都是在一个线程中进行的。但是对于不同的servlet request,使用的线程不一定是同一个线程(有一个线程的可能性,因为可能会从线程池中拿到同一个线程)
MODE_GLOBAL工作模式,数据保存在一个静态变量中,其存储载体是一个静态变量,可以在多线程环境下使用,但是用的比较少。
MODE_INHERITABLETHREADLOCAL工作模式,其存储载体为InheritableThreadLocal,InheritableThreadLocal继承ThreadLocal,多了一个特性,就是在创建子进程的时候,会自动的将父进程中的数据复制到子进程中去,实现了子进程能够获取父进程数据的功能。
所以,如果使用的是Spring Security 5.0及以上版本,且需要在异步线程获取用户信息的时候,可以考虑修改SecurityContextHolder的默认策略。
需要注意的是,SecurityContextHolder.setStrategyName 这个方法通常是在启动时被调用的,所以你应该在你的启动类或者配置类中设置这个策略。
在Spring Boot应用中,如果程序是以jar包的方式运行,你可以在主类中设置这个策略:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
SpringApplication.run(MyApplication.class, args);
}
}
或者,如果你使用的是配置类,你可以在配置类中设置策略:
import javax.annotation.PostConstruct;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.context.annotation.Configuration;
/**
* Spring Security配置
*/
@Configuration
public class SpringSecurityConfig {
/**
* 项目启动时,初始化配置:设置安全上下文(SecurityContext)的存储策略
*/
@PostConstruct
public void setStrategyName(){
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);// 可继承的安全上下文
}
}
这样,你就可以在应用中使用可继承的安全上下文了。