Spring Security使用接口SecurityContext抽象建模"安全上下文"这一概念。这里安全上下文SecurityContext指的是当前执行线程使用的最少量的安全信息(其实就是用于代表访问者账号的有关信息)。
publicinterfaceSecurityContextextendsSerializable{AuthenticationgetAuthentication();voidsetAuthentication(Authentication authentication);}
Spring Security为接口SecurityContext提供的缺省实现SecurityContextImpl,该实现逻辑其实很简单,主要就是保持一个Authentication`对象
当一个线程在服务用户期间,该安全上下文对象会保存在SecurityContextHolder中。
publicclassSecurityContextHolder{publicstaticfinalString MODE_THREADLOCAL="MODE_THREADLOCAL";publicstaticfinalString MODE_INHERITABLETHREADLOCAL="MODE_INHERITABLETHREADLOCAL";publicstaticfinalString MODE_GLOBAL="MODE_GLOBAL";publicstaticfinalString SYSTEM_PROPERTY="spring.security.strategy";privatestaticString strategyName=System.getProperty(SYSTEM_PROPERTY);privatestaticSecurityContextHolderStrategy strategy;//默认是ThreadLocalSecurityContextHolderStrategyprivatestaticint initializeCount=0;static{initialize();}//忽略代码....privatestaticvoidinitialize(){if(!StringUtils.hasText(strategyName)){// Set default strategyName= MODE_THREADLOCAL;}if(strategyName.equals(MODE_THREADLOCAL)){ strategy=newThreadLocalSecurityContextHolderStrategy();}elseif(strategyName.equals(MODE_INHERITABLETHREADLOCAL)){ strategy=newInheritableThreadLocalSecurityContextHolderStrategy();}elseif(strategyName.equals(MODE_GLOBAL)){ strategy=newGlobalSecurityContextHolderStrategy();}else{// Try to load a custom strategytry{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。
publicstaticvoidsetStrategyName(String strategyName){SecurityContextHolder.strategyName= strategyName;initialize();}
其二,通过设置属性值 spring.security.strategy。
publicstaticfinalString SYSTEM_PROPERTY="spring.security.strategy";privatestaticString strategyName=System.getProperty(SYSTEM_PROPERTY);
publicinterfaceSecurityContextHolderStrategy{voidclearContext();SecurityContextgetContext();voidsetContext(SecurityContext context);SecurityContextcreateEmptyContext();}
线程本地模式
对应用中的某个线程保持一个SecurityContext,这种模式下,应用中的每个线程同一时间通过SecurityContextHolder访问到的都是关于自己线程的SecurityContext;
finalclassThreadLocalSecurityContextHolderStrategyimplementsSecurityContextHolderStrategy{//使用ThreadLocal存储安全上下文privatestaticfinalThreadLocal<SecurityContext> contextHolder=newThreadLocal<>();publicvoidclearContext(){ contextHolder.remove();}publicSecurityContextgetContext(){SecurityContext ctx= contextHolder.get();if(ctx==null){ ctx=createEmptyContext(); contextHolder.set(ctx);}return ctx;}publicvoidsetContext(SecurityContext context){Assert.notNull(context,"Only non-null SecurityContext instances are permitted"); contextHolder.set(context);}publicSecurityContextcreateEmptyContext(){returnnewSecurityContextImpl();}}
线程继承模式
InheritableThreadLocal 相较于 ThreadLocal,多了子线程可以继承父线程的属性的特性,但是,针对普通WEB应用,应该是英雄无用武之地。
finalclassInheritableThreadLocalSecurityContextHolderStrategyimplementsSecurityContextHolderStrategy{privatestaticfinalThreadLocal<SecurityContext> contextHolder=newInheritableThreadLocal<>();//忽略代码.....}
全局模式
对整个应用公开保持一个SecurityContext,这种模式下,应用中的多个线程同一时间通过SecurityContextHolder访问到的都会是同一个SecurityContext对象;
finalclassGlobalSecurityContextHolderStrategyimplementsSecurityContextHolderStrategy{privatestaticSecurityContext contextHolder;publicvoidclearContext(){ contextHolder=null;}publicSecurityContextgetContext(){if(contextHolder==null){ contextHolder=newSecurityContextImpl();}return contextHolder;}publicvoidsetContext(SecurityContext context){Assert.notNull(context,"Only non-null SecurityContext instances are permitted"); contextHolder= context;}publicSecurityContextcreateEmptyContext(){returnnewSecurityContextImpl();}}
publicclassMySecurityContextHolderStrategyimplementsSecurityContextHolderStrategy{privatestaticfinalThreadLocal<SecurityContext> contextHolder=newThreadLocal<>();publicvoidclearContext(){ contextHolder.remove();}publicSecurityContextgetContext(){SecurityContext ctx= contextHolder.get();if(ctx==null){ ctx=createEmptyContext(); contextHolder.set(ctx);}return ctx;}publicvoidsetContext(SecurityContext context){Assert.notNull(context,"Only non-null SecurityContext instances are permitted"); contextHolder.set(context);}publicSecurityContextcreateEmptyContext(){returnnewSecurityContextImpl();}}
MySecurityContextHolderStrategy的注册
@ConfigurationpublicclassMyWebSecurityConfigextendsWebSecurityConfigurerAdapter{static{SecurityContextHolder.setStrategyName("learinning.securityextend.MySecurityContextHolderStrategy");}//忽略代码....}
测试
@RestController@RequestMapping("demo")publicclassDemoController{@GetMapping("test")// @RolesAllowed({"admin"})publicStringtest1(){SecurityContextHolderStrategy context=SecurityContextHolder.getContextHolderStrategy();return"test1";}}