Spring Security核心组件之安全上下文

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();
}

Spring Security核心组件之安全上下文
线程本地模式

对应用中的某个线程保持一个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";
    }
}

Spring Security核心组件之安全上下文

上一篇:springboot整合springsecrity的使用


下一篇:spring boot security 安全权限 ---2 短信/图片验证码 认证授权