当前位置: 首页 > news >正文

SpringSecurity原理解析(一)

一、SpringSecurity 核心组件

       在SpringSecurity中的jar包有4个,作用分别为:

spring-security-coreSpringSecurity的核心jar包,认证和授权的核心代码都在这里面
spring-security-config如果使用Spring Security XML名称空间进行配置或Spring Security的Java configuration支持,则需要它
spring-security-web用于Spring Security web身份验证服务和基于url的访问控制
spring-security-test测试单元

1、Authentication

      Authentication 是 org.springframework.security.core 包下的一个接口,

      Authentication 表示一个当前认证的对象,继承了接口 Principal,接口Principal 用来

     表示一个主题的抽象概念,可以用来表示任何实体对象,如:个人、公司、登录id等,

     简单的来说 Principal 可以表示任何具体的对象。

      Authentication 定义的方法如下:

public interface Authentication extends Principal, Serializable {// 获取认证用户拥有的对应的权限Collection<? extends GrantedAuthority> getAuthorities();// 获取用户的凭证(认证)Object getCredentials();// 存储有关身份验证请求的其他详细信息。这些可能是 IP地址、证书编号等//即获取认证用户的详细信息Object getDetails();// 获取用户信息 通常是 UserDetails 对象Object getPrincipal();// 判断当前用户的登录状态boolean isAuthenticated();// 设置认证状态void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;}

     

2、SecurityContextHolder

2.1、SecurityContext

     既然上面说 Authentication 表示一个认证对象,那么Authentication对象是如何放到 

     SpringSecurity 中的?

     SecurityContext 表示 SpringSecurity 上下文对象(也可看成是一个容器);

     SpringSecurity 是包 org.springframework.security.core.context 中一个接口,定义如下:

public interface SecurityContext extends Serializable {//获取Authentication Authentication getAuthentication();//保存Authentication void setAuthentication(Authentication authentication);}

     通过 SecurityContext 的定义可以发现, SecurityContext就干了2件事情,即:

              1)保存 Authentication

              2)获取 Authentication

   

2.2、SecurityContextHolder

         下面来看看在spring-security-core中的SecurityContextHolder,这个是一个非常基础的

          对象,存储了当前应用的上下文SecurityContext,而在SecurityContext可以获取

          Authentication对象。也就是当前认证的相关信息会存储在Authentication对象中。

          另外 SecurityContextHolder 还定义了 SecurityContext 的存储方式。

          SecurityContextHolder 定义如下:

public class SecurityContextHolder {//下边2个常量表示 SecurityContext  存储模式public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";public static final String MODE_GLOBAL = "MODE_GLOBAL";//配置文件中自定义strategy时,配置项的名称public static final String SYSTEM_PROPERTY = "spring.security.strategy";private static String strategyName = System.getProperty(SYSTEM_PROPERTY);//用于存储SecurityContext private static SecurityContextHolderStrategy strategy;//SecurityContextHolder 初始化次数private static int initializeCount = 0;static {initialize();}/*** 清除保存的SecurityContext */public static void clearContext() {strategy.clearContext();}/*** 获取 SecurityContext * 注意这个方法是static 静态方法,可由类直接调用* @return the security context (never <code>null</code>)*/public static SecurityContext getContext() {return strategy.getContext();}/*** 返回初始化次数*/public static int getInitializeCount() {return initializeCount;}//初始化private static void initialize() {//判断配置文件中是否指定 strategy的实现类,若没指定则使用 MODE_THREADLOCALif (!StringUtils.hasText(strategyName)) {// Set defaultstrategyName = MODE_THREADLOCAL;}if (strategyName.equals(MODE_THREADLOCAL)) {//表示把SecurityContext 存储在当前线程的 ThreadLocal中,线程之间隔离strategy = new ThreadLocalSecurityContextHolderStrategy();}else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {//表示SecurityContext 在父子线程之间可以共享strategy = new InheritableThreadLocalSecurityContextHolderStrategy();}else if (strategyName.equals(MODE_GLOBAL)) {//表示把SecurityContext 存储在全局变量中,全局共享strategy = new GlobalSecurityContextHolderStrategy();}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++;}/*** 保存 SecurityContext */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();}@Overridepublic String toString() {return "SecurityContextHolder[strategy='" + strategyName + "'; initializeCount="+ initializeCount + "]";}
}

         默认情况下,SecurityContextHolder是通过 `ThreadLocal`来存储对应的信息的。也就是

         在一个线程中我们可以通过这种方式来获取当前登录的用户的相关信息。而在

         SecurityContext中就只提供了对Authentication对象操作的方法

2.3、SecurityContextHolderStrategy

        在上边可以发现 SecurityContext 真正保存在 SecurityContextHolderStrategy 中的。

        SecurityContextHolderStrategy有三个是实现类,分别是:

                       

           

GlobalSecurityContextHolderStrategy把SecurityContext存储为static变量,全局共享
InheritableThreadLocalSecurityContextStrategy把SecurityContext存储在InheritableThreadLocal中
InheritableThreadLocal解决父线程生成的变量传递到子线程中进行使用
ThreadLocalSecurityContextStrategy把SecurityContext存储在ThreadLocal中,只有当前线程可以使用

2.4、Authentication、SecurityContext、SecurityContextHolder三者之间的关系

        SecurityContext 存储保存 Authentication,SecurityContextHolder存储并保存

         SecurityContext,通过SecurityContextHolder可以获取到 Authentication

         即如下图所示:

                 

        明白了3者之间的关系,下面可以通过 SecurityContextHolder 来获取当前登录的用户信息

        示例代码如下:

public String getLoginUser(){Authentication authentication = SecurityContextHolder.getContext().getAuthentication();Object principal = authentication.getPrincipal();if(principal instanceof UserDetails){UserDetails userDetails = (UserDetails) principal;System.out.println(userDetails.getUsername());return "当前登录的账号是:" + userDetails.getUsername();}return "当前登录的账号-->" + principal.toString();}

         调用 getContext()返回的对象是 SecurityContext接口的一个实例,这个对象默认是保

         存在线程中的。接下来将看到,Spring Security中的认证大都返回一个 UserDetails的实

          例作为Principa。

3、UserDetailsService

      在上面的关系中我们看到在Authentication中存储当前登录用户的是Principal对象,而通常

     情况下Principal对象可以转换为UserDetails对象。UserDetails是Spring Security中的一个

     核心接口。它表示一个Principal,但是是可扩展的、特定于应用的。可以认为 UserDetails

     是数据库中用户表记录和Spring Security在 SecurityContextHolder中所必须信息的适配器。

     UserDetails 接口定义如下

public interface UserDetails extends Serializable {// 对应的权限Collection<? extends GrantedAuthority> getAuthorities();// 密码String getPassword();// 账号String getUsername();// 账号是否过期boolean isAccountNonExpired();// 是否锁定boolean isAccountNonLocked();// 凭证是否过期boolean isCredentialsNonExpired();// 账号是否可用boolean isEnabled();}

     UserDetails 接口的默认实现是类User,如下图所示:

              

     那么问题来了,这个UserDetails对象什么时候提供给SecurityContextHolder呢?

            这就需要用到接口UserDetailsService,在用户认证时我们需要实现UserDetailsService

     ,在UserDetailsService的方法 loadUserByUsername 中,将从数据库查询到的用户信息封

     装成User对象。

     UserDetailsService接口定义如下:

public interface UserDetailsService {UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;}

      Spring Security提供了许多 UserDetailsSerivice接口的实现,包括使用内存中map的实现

    (InMemoryDaoImpl低版本 InMemoryUserDetailsManager)和使用JDBC的实现

    (JdbcDaoImpl)。但在实际开发中我们更喜欢自己来编写,比如UserServiceImpl我们的案例

       UserDetailsService接口的实现有如下:

               

      UserServiceImpl我们的案例代码如下:

/*** 用户认证接口* todo 注意:*    继承 UserDetailsService*/
public interface UserService extends UserDetailsService {
}@Service
public class UserServiceImpl implements UserService {@Autowiredprivate SysUserMapper sysUserMapper;/*** 根据账号查询用户信息,并进行用户认证* @param userName* @return* @throws UsernameNotFoundException*/public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {//根据账号查询用户信息SysUser sysUser = sysUserMapper.queryByUserName(userName);//账号存在if(sysUser != null){/*** 封装用户的权限*/List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();//添加用户的权限,list.add(new SimpleGrantedAuthority("ROLE_USER"));// 表示账号存在UserDetails userDetails = new User(sysUser.getUserName() // 账号,sysUser.getPasseord() // 密码,{noop}表示不加密,true  //表示当前账号可用,false //表示当前账号是否过期,false=过期,true //表示凭证是否过期,true=未过期,true //用户是否被锁定,true=未被锁定,list);return userDetails;}//用户不存在return null;}public static void main(String[] args) {//BCryptPasswordEncoder是Spring Security 提供的密码加密方式BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();String password = "admin";System.out.println(encoder.encode(password));}
}

      

4、GrantedAuthority

      我们在Authentication中看到不光关联了Principal还提供了一个getAuthorities()方法来获取

      对应的GrantedAuthority对象数组。和权限相关,后面在权限模块详细讲解

       GrantedAuthority定义如下:

public interface GrantedAuthority extends Serializable {//获取权限信息String getAuthority();}

5、总结

     上面介绍到的核心对象总结

Authentication特定于Spring Security的principal
SecurityContext存放了Authentication和特定于请求的安全信息
SecurityContextHolder用于获取SecurityContext
UserDetails提供从应用程序的DAO或其他安全数据源构建Authentication对象所需的信息
UserDetailsService接受String类型的用户名,创建并返回UserDetail
GrantedAuthority对某个principal的应用范围内的授权许可

相关文章:

SpringSecurity原理解析(一)

一、SpringSecurity 核心组件 在SpringSecurity中的jar包有4个&#xff0c;作用分别为&#xff1a; spring-security-coreSpringSecurity的核心jar包&#xff0c;认证和授权的核心代码都在这里面spring-security-config如果使用Spring Security XML名称空间进行配置或Spring S…...

在Ubuntu 20.04上安装Nginx的方法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 简介 Nginx 是世界上最流行的 Web 服务器之一&#xff0c;负责托管互联网上一些最大和流量最高的网站。它是一个轻量级选择&#xff0c…...

基于苹果Vision Pro的AI NeRF方案:MetalSplatter

随着苹果Vision Pro的发布,混合现实(Mixed Reality, MR)技术迎来了一个新的发展阶段。为了充分利用Vision Pro的潜力,一款名为MetalSplatter的Swift/Metal库应运而生,它允许开发者在Vision Pro上以全立体的方式体验捕捉内容。本文将详细介绍MetalSplatter的特点及其如何为…...

linux系统中,计算两个文件的相对路径

realpath --relative-to/home/itheima/smartnic/smartinc/blocks/ruby/seanet_diamond/tb/parser/test_parser_top /home/itheima/smartnic/smartinc/corundum/fpga/lib/eth/lib/axis/rtl/axis_fifo.v 检验方式就是直接在当前路径下&#xff0c;把输出的路径复制一份&#xff0…...

[数据集][目标检测]抽烟检测数据集VOC+YOLO格式22559张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;22559 标注数量(xml文件个数)&#xff1a;22559 标注数量(txt文件个数)&#xff1a;22559 标…...

C和指针:结构体(struct)和联合(union)

结构体和联合 结构体 结构体包含一些数据成员&#xff0c;每个成员可能具有不同的类型。 数组的元素长度相同&#xff0c;可以通过下标访问(转换为指针)。但是结构体的成员可能长度不同&#xff0c;所以不能用下标来访问它们。成员有自己的名字&#xff0c;可以通过名字访问…...

[数据集][目标检测]电动车头盔佩戴检测数据集VOC+YOLO格式4235张5类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4235 标注数量(xml文件个数)&#xff1a;4235 标注数量(txt文件个数)&#xff1a;4235 标注…...

软件工程知识点总结(2):需求分析(一)——用例建模

1 软件项目开发流程&#xff1a; 需求分析→概要设计→详细设计→编码实施→测试→产品提 交→维护 2 系统必须做什么&#xff1f; 获取用户需求&#xff0c;从用户角度考虑&#xff0c;用户需要系统必须完成哪些工作&#xff0c;也就是对目 标系统提出完整、准确、清晰、具体…...

2024 年高教社杯全国大学生数学建模竞赛C题—农作物的种植策略(讲解+代码+成品论文助攻,均已更新完毕)

2024数学建模国赛选题建议团队助攻资料-CSDN博客文章浏览阅读1k次&#xff0c;点赞20次&#xff0c;收藏24次。通过分析5个题目的特点&#xff0c;可知数学建模常用的模型大概可以分为五大类——https://blog.csdn.net/qq_41489047/article/details/141925859 本次国赛white学长…...

?.操作符是什么

?.操作符在不同的编程语言和上下文中可能有不同的含义和用途&#xff0c;但一般来说&#xff0c;它并不是一个广泛存在于所有编程语言中的标准操作符。不过&#xff0c;基于一些编程语言的特性和习惯&#xff0c;我们可以对?.操作符进行一些推测和解释。 1. 可选链操作符&am…...

ArcGIS出图格网小数位数设置

1、比如要去掉格网后面的小数点&#xff0c;如何设置呢&#xff1f; 2、如下图设置。...

数学建模_缺失值处理_拉格朗日、牛顿插值(全)

- 缺失值处理 1. 识别缺失值 在处理缺失值之前&#xff0c;首先需要识别数据中的缺失值。 1.1 使用 isna() 和 isnull() Pandas 提供了 isna() 和 isnull() 方法来检测缺失值&#xff0c;二者功能相同。 import pandas as pddf pd.DataFrame({A: [1, 2, None, 4],B: [None, 2,…...

算法题之水壶问题

水壶问题 有两个水壶&#xff0c;容量分别为 x 和 y 升。水的供应是无限的。确定是否有可能使用这两个壶准确得到 target 升。 你可以&#xff1a; 装满任意一个水壶清空任意一个水壶将水从一个水壶倒入另一个水壶&#xff0c;直到接水壶已满&#xff0c;或倒水壶已空。 示…...

Java项目: 基于SpringBoot+mysql蜗牛兼职网兼职平台管理系统(含源码+数据库+答辩PPT+毕业论文)

一、项目简介 本项目是一套基于SpringBootmysql蜗牛兼职网兼职平台管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操…...

C#数组中的Rank,GetUpperBound(), GetLength()

Rank-数组的秩&#xff0c;一维数组的Rank1&#xff1b;二维数组的Rank2&#xff1b; GetUpperBound()--获取每一维的索引的上限&#xff0c; 比如int[4,5], 那么GetUpperBound(0) 3; GetUpperBound(1) 4 ; 所以 对于二维数组来说 GetUpperBound(0)1行数&#xff1b; G…...

Android应用开发项目式教程——序

文章目录 Android技术本书特点本书内容本书参考 Android技术 Android是重要的客户端技术&#xff0c;因其开源开放的特点&#xff0c;Android在其初期就迅速成长为智能手机的主流操作系统&#xff0c;近年来更进一步成为智能电视、智能车载终端等智能设备的主流操作系统&#…...

【Spring Boot 3】【Web】统一处理 HTTP 请求体

【Spring Boot 3】【Web】统一处理 HTTP 请求体 背景介绍开发环境开发步骤及源码工程目录结构总结背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总…...

uni-app开发微信小程序

uni-app 是一个使用 Vue.js 开发所有前端应用的框架&#xff0c;它允许开发者编写一次代码&#xff0c;然后发布到iOS、Android、Web&#xff08;包括各种小程序平台如微信小程序、支付宝小程序、百度智能小程序等&#xff09;以及各种快应用平台上。对于使用uni-app开发微信小…...

Qt开发框架--完整的软件开发框架

Qt开发框架包含一整套高度直观、模块化 的C 库类&#xff0c;并加载可简化应用程序开发的API。Qt 可生成高可读、易维护和可重用的代码&#xff0c;具有较高的运行时性能&#xff0c;且内存占用小。最重要的是&#xff0c;Qt是跨平台的。 Qt工具分为这么几个类别&#xff1a; …...

Python爬虫-Amazon亚马逊oData参数

前言 本文是该专栏的第37篇,后面会持续分享python爬虫干货知识,记得关注。 本文以“亚马逊Amazon”为例,主要获取亚马逊商品详情页的oData参数规律。 具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。接下来,跟着笔者直接往下看正文详细内容。(附带完整…...

Q215 数组中第K大的元素

思路 可以用排序&#xff0c;但是不用全有序 还有个要求是O&#xff08;n&#xff09; 快排改版 快排只排需要的部分 public int findKthLargest(int[] nums, int k) {return quickSort(nums, 0, nums.length-1, nums.length-k);}public static int quickSort(int[] nums, …...

Java8特性:分组、提取字段、去重、过滤、差集、交集

总结下自己使用过的特性 将对象集合根据某个字段分组 //根据id分组 Map<String, List<Bean>> newMap successCf.stream().collect(Collectors.groupingBy(b -> b.getId().trim()));获取对象集合里面的某个字段的集合 List<Bean> list new ArrayList&l…...

Maven快速上手使用指南的笔记

Maven Mini Guides Configuring for Reproducible Builds 使用Maven实现重复构建。 检查当前使用的插件的版本。 mvn artifact:check-buildplan修改pom.xml&#xff0c;增加如下配置&#xff0c;显式指定project.build.outputTimestamp的取值&#xff1a; <properties>…...

MySQL面试题大全和详解,含SQL例子

若有不理解&#xff0c;可以问一下这几个免费的AI网站 https://ai-to.cn/chathttp://m6z.cn/6arKdNhttp://m6z.cn/6b1quhhttp://m6z.cn/6wVAQGhttp://m6z.cn/63vlPw 下面是一些常见的 MySQL 面试题及其解答&#xff0c;包含 SQL 示例。 1. 什么是 MySQL&#xff1f; 答&…...

java-redis-雪崩

Redis 雪崩问题 Redis雪崩 是指在 Redis 缓存系统中&#xff0c;当大量缓存同时失效时&#xff0c;所有请求直接打到数据库&#xff0c;导致数据库瞬间压力激增&#xff0c;甚至崩溃的现象。雪崩问题通常出现在高并发的系统中&#xff0c;因为缓存的失效导致后端数据库承受不了…...

如何在mac上玩使命召唤手游?苹果电脑好玩的第一人称射击游戏推荐

《使命召唤4&#xff1a;现代战争》&#xff08;Call of Duty 4: Modern Warfare&#xff09;是由Infinity Ward开发并于2007年发行的第一人称射击游戏。该游戏是《使命召唤》系列的第四部作品&#xff0c;是一款非常受欢迎的游戏之一&#xff0c;《使命召唤4&#xff1a;现代战…...

SimHash算法详解与应用

1. 简介 在当今信息爆炸的时代&#xff0c;如何有效地管理和处理海量的文本数据&#xff0c;尤其是去除重复内容&#xff0c;是一项重要的任务。SimHash 是一种巧妙的哈希算法&#xff0c;它不仅能快速生成文本的哈希值&#xff0c;还能在不同文本之间生成相似的哈希值&#x…...

RasberryPi 3B树莓派基本配置

RaspberryPi 3B树莓派基本配置 文章目录 RaspberryPi 3B树莓派基本配置一、准备工作1.1 硬件准备&#xff1a;1.1.1 树莓派和电源适配器&#xff1a;1.1.2 USB转TTL模块&#xff1a;1.1.3 读卡器和TF卡&#xff1a; 1.2 软件准备&#xff1a;1.2.1 下载 Raspberry Pi OS&#x…...

Docker编译环境的使用(ubuntu)

目录 Ubuntu安装docker 重启docker 拉取镜像 进入docker安装软件 提交docker 添加用户到docker组 进入docker 添加build用户 停止容器 保存docker镜像 load镜像 删除容器 Ubuntu安装docker sudo apt install docker.io 国内可用的源 Welcome to nginx! (tence…...

认知杂谈53

今天分享 有人说的一段争议性的话 I I 1.自助者天助 首先呢&#xff0c;咱得好好琢磨琢磨“自助者天助”这句话。这话说起来好像有点高深莫测的感觉&#xff0c;其实啊&#xff0c;道理特别简单。 就是说要是你自己都不乐意努力&#xff0c;那老天爷也不会平白无故地来帮你…...