SpringSecurity之SecurityContextHolder使用解读

 更新时间:2023年03月17日 14:08:18   作者:chihaihai  
这篇文章主要介绍了SpringSecurity之SecurityContextHolder使用解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

之前在使用SpringSecurity的过程中并没有把很多东西理解透彻,找了很多学习资料也都只是是很浅显了告诉你怎么使用这个东西。现在只能自己回过头来研究研究。。。。终究是一个人扛下了所有/(ㄒoㄒ)/~~。

GO,现在越看spring源码越觉得里面注释是真的详细,虽然英语很菜耐不住有翻译哈哈哈。

介绍

看了几篇大佬的技术博客里面都介绍到,SecurityContextHolder是保存全上下文对象(SecurityContext)的地方。对于这种说法我感觉是完全不正确的,如果后面有新的认知我会回来进行修改。

我认为SecurityContextHolder只是为SecurityContext提供一种存储策略,只是主导了他的存储方式及地址。

使用中我们表面上看起来SecurityContext的存储都是通过SecurityContextHolder在控制人们就习以为常的说成了Security Context存储在了SecurityContextHolder中,至于真相我们看源码慢慢分析。

public class SecurityContextHolder {
	// ~ Static fields/initializers
	// =====================================================================================

	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;
	private static int initializeCount = 0;

首先从源码可以看到SecurityContextHolder提供了一个SecurityContextHolderStrategy存储策略进行上下文的存储,进入到Security ContextHolderStrategy接口,由下图我们可以清晰的看到其总共有三个实现类。

分别对应三种存储策略,这里我不知道为什么99的文章都只说了threadlocal和global两种。

要不是看了下实现类我都信了。

在这里插入图片描述

就跟字面意思一样三种策略分别对应threadlocal,global,InheritableThreadLocal三种方式。

继续回到源码我们详细看这三种方式。

	private static void initialize() {
		if (!StringUtils.hasText(strategyName)) {
			// 如果没有设置自定义的策略,就采用MODE_THREADLOCAL模式
			strategyName = MODE_THREADLOCAL;
		}
			// ThreadLocal策略
		if (strategyName.equals(MODE_THREADLOCAL)) {
			strategy = new ThreadLocalSecurityContextHolderStrategy();
		}
		// 采用InheritableThreadLocal,它是ThreadLocal的一个子类
		else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
			strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
		}
		// 全局策略,实现方式就是static SecurityContext contextHolder
		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 默认使用的是THREADLOCAL模式,光从名字看感觉就是存储在threadlocal中的策略到底是不是我们去源码中分别一探究竟:

ThreadLocalSecurityContextHolderStrategy

final class ThreadLocalSecurityContextHolderStrategy implements
		SecurityContextHolderStrategy {
	// ~ Static fields/initializers
	// =====================================================================================

	private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
	。。。。。。
}

InheritableThreadLocalSecurityContextHolderStrategy

final class InheritableThreadLocalSecurityContextHolderStrategy implements
		SecurityContextHolderStrategy {
	// ~ Static fields/initializers
	// =====================================================================================

	private static final ThreadLocal<SecurityContext> contextHolder = new InheritableThreadLocal<>();
	。。。。。。
}

GlobalSecurityContextHolderStrategy

final class GlobalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
	// ~ Static fields/initializers
	// =====================================================================================

	private static SecurityContext contextHolder;
	。。。。。。
}

到这里那些说SecurityContext存储在SecurityContextHolder的大佬们我认为应该是不严谨的。

默认是将SecurityContext存储在threadlocal中,可能是spring考虑到目前大多数为BS应用,一个应用同时可能有多个使用者,每个使用者又对应不同的安全上下,Security Context Holder为了保存这些安全上下文。

缺省情况下,使用了ThreadLocal机制来保存每个使用者的安全上下文。

因为缺省情况下根据Servlet规范,一个Servlet request的处理不管经历了多少个Filter,自始至终都由同一个线程来完成。这样就很好的保证了其安全性。

但是当我们开发的是一个CS本地应用的时候,这种模式就不太适用了。

spring早早的就考虑到了这种情况,这个时候我们就可以设置为Global模式仅使用一个变量来存储SecurityContext。比如还有其他的一些应用会有自己的线程创建,并且希望这些新建线程也能使用创建者的安全上下文。

这种效果,我们就可以通过将SecurityContextHolder配置成MODE_INHERITABLETHREADLOCAL策略达到。

SecurityContext又到底是个什么东西呢?稍后新开一遍学习记录。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringBoot配置及使用Schedule过程解析

    SpringBoot配置及使用Schedule过程解析

    这篇文章主要介绍了SpringBoot配置及使用Schedule过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • Java 实战练手项目之酒店管理系统的实现流程

    Java 实战练手项目之酒店管理系统的实现流程

    读万卷书不如行万里路,只学书上的理论是远远不够的,只有在实战中才能获得能力的提升,本篇文章手把手带你用java+SSM+jsp+mysql+maven实现一个酒店管理系统,大家可以在过程中查缺补漏,提升水平
    2021-11-11
  • IDEA中JetBrains Mono字体的正确安装姿势

    IDEA中JetBrains Mono字体的正确安装姿势

    在 JetBrains Mono 的设计阶段,它就充分考虑到了长时间工作可能导致的眼睛疲劳问题,比如字母的大小和形状、空间量、自然等宽平衡、不必要的细节、连字、以及难以区分的符号等,从而最终设计出了这么一款字体
    2021-06-06
  • JAVA面试题 start()和run()详解

    JAVA面试题 start()和run()详解

    这篇文章主要介绍了JAVA面试题 启动线程是start()还是run()?为什么?,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • Java基础之文件概述

    Java基础之文件概述

    这篇文章主要介绍了Java基础之文件概述,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有一定的帮助,需要的朋友可以参考下
    2021-05-05
  • IDEA代码规范&质量检查的实现

    IDEA代码规范&质量检查的实现

    这篇文章主要介绍了IDEA代码规范&质量检查的实现,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • Java concurrency集合之CopyOnWriteArraySet_动力节点Java学院整理

    Java concurrency集合之CopyOnWriteArraySet_动力节点Java学院整理

    CopyOnWriteArraySet基于CopyOnWriteArrayList实现,其唯一的不同是在add时调用的是CopyOnWriteArrayList的addIfAbsent(若没有则增加)方法
    2017-06-06
  • Spring Validator从零掌握对象校验的详细过程

    Spring Validator从零掌握对象校验的详细过程

    SpringValidator学习指南从零掌握对象校验,涵盖Validator接口、嵌套对象处理、错误代码解析等核心概念,帮助开发者实现数据校验的规范与高效,本文详细介绍Spring Validator从零掌握对象校验,感兴趣的朋友一起看看吧
    2025-02-02
  • SpringBoot使用@Async注解可能会遇到的8大坑点汇总

    SpringBoot使用@Async注解可能会遇到的8大坑点汇总

    SpringBoot中,@Async注解可以实现异步线程调用,用法简单,体验舒适,但是你一定碰到过异步调用不生效的情况,今天,我就列出90%的人都可能会遇到的8大坑点,需要的朋友可以参考下
    2023-09-09
  • MybatisPlus整合Flowable出现的坑及解决

    MybatisPlus整合Flowable出现的坑及解决

    这篇文章主要介绍了MybatisPlus整合Flowable出现的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03

最新评论