学向勤中得,萤窗万卷书。这篇文章主要讲述#yyds干货盘点#SecurityContextHolder之策略模式源码分析相关的知识,希望能为你提供帮助。
springsecurity中,用户登录信息本质是保存到HttpSession中,springsecurity进行封装 获取登录数据有两种思路:
- 从SecurityContextHolder中获取
- 从当前请求对象中获取
@RestController
public class HelloController
@GetMapping("/hello")
public void hello()
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println("authentication.getClass() = " + authentication.getClass());
SecurityContextHolder存放的是SecurityContext ,SecurityContextHolder中定义三种不同的数据存储策略,采用了策略模式
- MODE_THREADLOCAL :将SecurityContext放在ThreadLocal中,开启子线程,子线程获取不到用户数据。
- MODE_INHERITABLETHREADLOCAL:多线程环境,子线程也能获取到用户数据。
- MODE_GLOBAL:数据保存到一个静态变量中,web开发中很少使用。
public interface SecurityContextHolderStrategy
void clearContext();
SecurityContext getContext();
void setContext(SecurityContext var1);
SecurityContext createEmptyContext();
有三个实现类 对应三个不同的存储策略
ThreadLocalSecurityContextHolderStrategy
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.security.core.context;
import org.springframework.util.Assert;
final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy
private static final ThreadLocal<
SecurityContext>
contextHolder = new ThreadLocal();
ThreadLocalSecurityContextHolderStrategy() public void clearContext()
contextHolder.remove();
public SecurityContext getContext()
SecurityContext ctx = (SecurityContext)contextHolder.get();
if (ctx == null)
ctx = this.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();
存储载体是ThreadLocal 针对SecurityContext的操作都是在ThreadLocal中进行操作。SecurityContext只是个接口,只有一个实现类是SecurityContextImpl
InheritableThreadLocalSecurityContextHolderStrategy
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.security.core.context;
import org.springframework.util.Assert;
final class InheritableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy
private static final ThreadLocal<
SecurityContext>
contextHolder = new InheritableThreadLocal();
InheritableThreadLocalSecurityContextHolderStrategy() public void clearContext()
contextHolder.remove();
public SecurityContext getContext()
SecurityContext ctx = (SecurityContext)contextHolder.get();
if (ctx == null)
ctx = this.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 ,InheritableThreadLocal继承ThreadLocal,多了一个特性,就是在子线程创建的时间,会自动将父线程的数据复制到子线程中。实现了子线程中能够获取登录数据的功能。
GlobalSecurityContextHolderStrategy
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.security.core.context;
import org.springframework.util.Assert;
final class GlobalSecurityContextHolderStrategy implements SecurityContextHolderStrategy
private static SecurityContext contextHolder;
GlobalSecurityContextHolderStrategy() 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();
存储载体是一个静态变量,也可以在多线程环境下使用,但用的较少。
SecurityContextHolder源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.security.core.context;
import java.lang.reflect.Constructor;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
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("spring.security.strategy");
private static SecurityContextHolderStrategy strategy;
private static int initializeCount = 0;
public SecurityContextHolder() public static void clearContext()
strategy.clearContext();
public static SecurityContext getContext()
return strategy.getContext();
public static int getInitializeCount()
return initializeCount;
private static void initialize()
if (!StringUtils.hasText(strategyName))
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
Class<
?>
clazz = Class.forName(strategyName);
Constructor<
?>
customStrategy = clazz.getConstructor();
strategy = (SecurityContextHolderStrategy)customStrategy.newInstance();
catch (Exception var2)
ReflectionUtils.handleReflectionException(var2);
++initializeCount;
public static void setContext(SecurityContext context)
strategy.setContext(context);
public static void setStrategyName(String strategyName)
SecurityContextHolder.strategyName = strategyName;
initialize();
public static SecurityContextHolderStrategy getContextHolderStrategy()
return strategy;
public static SecurityContext createEmptyContext()
return strategy.createEmptyContext();
public String toString()
return "SecurityContextHolder[strategy=" + strategyName + ";
initializeCount=" + initializeCount + "]";
static
initialize();
SecurityContextHolder 定义三个静态常量描述三种不同存储策略,在静态代码块中初始化,根据不同的strategyName初始化不同的存储策略,可以调用配置系统变量或者调用setStrategyName改变策略。
在默认情况下,从子线程中获取登录数据是获取不到的,因为默认是MODE_THREADLOCAL策略。
存储策略在System.getProperty(" spring.security.strategy" ); 加载,可以配置系统变量来修改策略。
在VM option参数添加
-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL
【#yyds干货盘点#SecurityContextHolder之策略模式源码分析】SpringContextHolder默认将用户信息存储在ThreadLocal中,在SpringBoot中不同的请求使用不同的线程处理,是怎么获取到用户的信息的呢? 下篇文章揭晓,有懂的可以在评论区留言。
推荐阅读
- 自上而下的理解网络——HTTPS篇
- Dubbo | Dubbo快速上手笔记 - 环境与配置 #yyds干货盘点#
- #yyds干货盘点# 数据结构与算法之顺序表
- #私藏项目实操分享# SAP 电商云 Spartacus UI 的 checkout 场景中的串行请求设计分析
- 无快不破,在本地 docker 运行 IDEA 里面的项目()
- #yyds干货盘点#Vue-Router路由所有相关方法讲解(全),实战教学含登录拦截,建议收藏
- #yyds干货盘点#JavaScript之彻底理解原型与原型链
- #yyds干货盘点# InnoDB解决幻读的方案--LBCC&MVCC
- 迁移到新的Web主机后显示的WordPress模板