1、安全上下文
Spring Security使用接口SecurityContext抽象建模"安全上下文"这一概念。这里安全上下文SecurityContext指的是当前执行线程使用的最少量的安全信息(其实就是用于代表访问者账号的有关信息)。
public interface SecurityContext extends Serializable {
Authentication getAuthentication();
void setAuthentication(Authentication authentication);
}
Spring Security为接口SecurityContext提供的缺省实现SecurityContextImpl,该实现逻辑其实很简单,主要就是保持一个Authentication`对象
2、安全上下文管理器
当一个线程在服务用户期间,该安全上下文对象会保存在SecurityContextHolder中。
public class SecurityContextHolder {
public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
public static final String MODE_GLOBAL = "MODE_GLOBAL";
public static final String SYSTEM_PROPERTY = "spring.security.strategy";
private static String strategyName = System.getProperty(SYSTEM_PROPERTY);
private static SecurityContextHolderStrategy strategy;//默认是ThreadLocalSecurityContextHolderStrategy
private static int initializeCount = 0;
static {
initialize();
}
//忽略代码....
private static void initialize() {
if (!StringUtils.hasText(strategyName)) {
// Set default
strategyName = MODE_THREADLOCAL;
}
if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
}
else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
}
else if (strategyName.equals(MODE_GLOBAL)) {
strategy = new GlobalSecurityContextHolderStrategy();
}
else {
// Try to load a custom strategy
try {
Class<?> clazz = Class.forName(strategyName);
Constructor<?> customStrategy = clazz.getConstructor();
strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
initializeCount++;
}
//忽略代码....
}
SecurityContextHolder类提供的功能是保持SecurityContext,不过它的用法不是让使用者创建多个SecurityContextHolder对象,而是提供一组公开静态工具方法。其底层是感觉系统变量spring.security.strategy
的值来判断使用何种策略SecurityContextHolderStrategy
SecurityContextHolder 类有2种方式初始化 SecurityContextHolderStrategy。
其一,通过其静态方法setStrategyName。
public static void setStrategyName(String strategyName) {
SecurityContextHolder.strategyName = strategyName;
initialize();
}
其二,通过设置属性值 spring.security.strategy。
public static final String SYSTEM_PROPERTY = "spring.security.strategy";
private static String strategyName = System.getProperty(SYSTEM_PROPERTY);
3、安全上下文生成器
public interface SecurityContextHolderStrategy {
void clearContext();
SecurityContext getContext();
void setContext(SecurityContext context);
SecurityContext createEmptyContext();
}
线程本地模式
对应用中的某个线程保持一个SecurityContext,这种模式下,应用中的每个线程同一时间通过SecurityContextHolder访问到的都是关于自己线程的SecurityContext;
final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
//使用ThreadLocal存储安全上下文
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
public void clearContext() {
contextHolder.remove();
}
public SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
public void setContext(SecurityContext context) {
Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
contextHolder.set(context);
}
public SecurityContext createEmptyContext() {
return new SecurityContextImpl();
}
}
线程继承模式
InheritableThreadLocal 相较于 ThreadLocal,多了子线程可以继承父线程的属性的特性,但是,针对普通WEB应用,应该是英雄无用武之地。
final class InheritableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static final ThreadLocal<SecurityContext> contextHolder = new InheritableThreadLocal<>();
//忽略代码.....
}
全局模式
对整个应用公开保持一个SecurityContext,这种模式下,应用中的多个线程同一时间通过SecurityContextHolder访问到的都会是同一个SecurityContext对象;
final class GlobalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static SecurityContext contextHolder;
public void clearContext() {
contextHolder = null;
}
public SecurityContext getContext() {
if (contextHolder == null) {
contextHolder = new SecurityContextImpl();
}
return contextHolder;
}
public void setContext(SecurityContext context) {
Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
contextHolder = context;
}
public SecurityContext createEmptyContext() {
return new SecurityContextImpl();
}
}
4、安全上下文生成器的自定义
public class MySecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
public void clearContext() {
contextHolder.remove();
}
public SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
public void setContext(SecurityContext context) {
Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
contextHolder.set(context);
}
public SecurityContext createEmptyContext() {
return new SecurityContextImpl();
}
}
MySecurityContextHolderStrategy的注册
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
static {
SecurityContextHolder.setStrategyName("learinning.securityextend.MySecurityContextHolderStrategy");
}
//忽略代码....
}
测试
@RestController
@RequestMapping("demo")
public class DemoController {
@GetMapping("test")
// @RolesAllowed({"admin"})
public String test1(){
SecurityContextHolderStrategy context = SecurityContextHolder.getContextHolderStrategy();
return "test1";
}
}