SpringSecurity - SecurityContextHolder 源码分析

概述

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

SecurityContext

/**
 * Interface defining the minimum security information associated with the current thread
 * of execution.
 *
 * <p>
 * The security context is stored in a {@link SecurityContextHolder}.
 * </p>
 */
public interface SecurityContext extends Serializable {

	Authentication getAuthentication();

	void setAuthentication(Authentication authentication);

}

这个接口只有一个实现类 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;

}

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 + "]";
	}

}

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

}

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

总结

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

  • SecurityContextHolder 为每一个执行中的线程关联了 SecurityContext
  • 在认证通过之后,应该通过 SecurityContextHolder 来设置 SecurityContext
  • 我们可以指定 SecurityContext 的存储策略,也可以自定义存储策略
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值