• SpringSecurity - SecurityContextHolder 源码分析


    概述

    SecurityContextHolder 可以拆分为两个单词 SecurityContextHolderHolder 意思是 持有 的意思,所以 SecurityContextHolder 的大概意思就是这个类中存放了 SecurityContext。也就是说我们首先要知道 SecurityContext 是什么

    SecurityContext

    /**
     * Interface defining the minimum security information associated with the current thread
     * of execution.
     *
     * 

    * The security context is stored in a {@link SecurityContextHolder}. *

    */
    public interface SecurityContext extends Serializable { Authentication getAuthentication(); void setAuthentication(Authentication authentication); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这个接口只有一个实现类 SecurityContextImpl,里面没什么内容,这里就不粘源码了。

    从源码注释中我们知道,SecurityContext 是一个接口,这个接口定义了与当前执行的线程所关联的最小安全信息,接口定义了两个方法,setget,针对 Authentication

    public interface Authentication extends Principal, Serializable {
    
    	// 授权信息
    	Collection<? extends GrantedAuthority> getAuthorities();
    
    	Object getCredentials();
    
    	Object getDetails();
    
    	// 认证主体
    	Object getPrincipal();
    	
    	boolean isAuthenticated();
    
    	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Authentication 继承自 Principal,而 PrincipalJava 提供的

    Authentication 是请求认证成功后的令牌,里面包含了认证主体 Principal 和授权信息 Authorities。知道了这两个概念以后,接下来我们看一下 SecurityContextHolder

    SecurityContextHolder

    public class SecurityContextHolder {
    
    	// 预设 3 种模式
    	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);
    
    	// SecurityContext 的持有策略接口,也就是上面 3 中模式的功能接口定义
    	private static SecurityContextHolderStrategy strategy;
    
    	// 初始化次数
    	private static int initializeCount = 0;
    
    	// 1、第一步,会执行 initialize 方法
    	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++;
    	}
    	
    	// 下面几个方式是定义的操作 SecurityContext 的方法,主要是对 SecurityContext 的增删改查
    	public static void clearContext() {
    		strategy.clearContext();
    	}
    
    	public static SecurityContext getContext() {
    		return strategy.getContext();
    	}
    
    	public static int getInitializeCount() {
    		return 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();
    	}
    
    	@Override
    	public String toString() {
    		return "SecurityContextHolder[strategy='" + strategyName + "'; initializeCount=" + initializeCount + "]";
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89

    SecurityContextHolder 的源码中我们知道默认是使用 ThreadLocalSecurityContextHolderStrategy 这种模式,也就是说底层是使用 ThreadLocal 来存储 SecurityContext 的。

    final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
    
    	private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
    
    	@Override
    	public void clearContext() {
    		contextHolder.remove();
    	}
    
    	@Override
    	public SecurityContext getContext() {
    		SecurityContext ctx = contextHolder.get();
    		if (ctx == null) {
    			ctx = createEmptyContext();
    			contextHolder.set(ctx);
    		}
    		return ctx;
    	}
    
    	@Override
    	public void setContext(SecurityContext context) {
    		Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
    		contextHolder.set(context);
    	}
    
    	@Override
    	public SecurityContext createEmptyContext() {
    		return new SecurityContextImpl();
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    源码比较简单,没什么内容,就不做过多介绍了。

    总结

    以上就是关于 SecurityContextHolder 的相关源码阅读,通过分析我们知道:

    • SecurityContextHolder 为每一个执行中的线程关联了 SecurityContext
    • 在认证通过之后,应该通过 SecurityContextHolder 来设置 SecurityContext
    • 我们可以指定 SecurityContext 的存储策略,也可以自定义存储策略
  • 相关阅读:
    答对这3个面试问题,薪资直涨20K
    30个Python操作小技巧
    快速新建springboot项目
    三项第一!天翼云通过DevSecOps能力成熟度评估认证
    2.通信机制-话题通讯 一直发,一直接收(电台-收音机)
    报错IDEA Terminated with exit code 1
    快讯:飞书玩家大会线上举行;微信支付推出“教培服务工具箱”
    动态规划——474. 一和零
    2014-2020年国有大型商业银行和全国股份制商业银行绿色信贷数据
    k8s中的端口hostPort、port、nodePort、targetPort
  • 原文地址:https://blog.csdn.net/qiaohao0206/article/details/126473498