【Shiro】Shiro 的学习教程(二)之认证、授权源码分析

目录

  • 1、背景
  • 2、相关类图
  • 3、解析
    • 3.1、加载、解析阶段
    • 3.2、认证阶段
    • 3.3、授权阶段

1、背景

继上节代码,通过 debug 进行 shiro 源码分析。

2、相关类图

debug 之前,先了解下一些类的结构图:

①:SecurityManager:安全管理器

在这里插入图片描述

  • DefaultSecurityManager
    • RememberMeManager:实现【记住我】功能
    • SubjectDAO:操作 Subject
    • SubjectFactory:Subject 工厂,用来生成 Subject
  • SessionsSecurityManager
    • SessionManager:用来管理 Session
  • AuthorizingSecurityManager
    • Authorizer:用来实现【授权】功能
  • AuthenticatingSecurityManager
    • Authenticator: 用来实现【认证】功能
  • RealmSecurityManager
    • Collection<Realm> realms:用来存储 Realm(由此可知:一个 SecurityManager 可以对应多个 Realm
  • CachingSecurityManager
    • CacheManager:用于实现【缓存】功能

②:Realm:数据域

在这里插入图片描述

  • IniRealm
    • resourcePath:ini 文件路径
    • Ini:将 ini 文件内容解析成 Ini 对象
  • TextConfigurationRealm
    • userDefinitions
    • roleDefinitions
  • SimpleAccountRealm
    • Map<String, SimpleAccount> users:存储 user
    • Map<String, SimpleRole> roles: 存储 role
  • AuthorizingRealm
    • boolean authorizationCachingEnabled: 是否进行授权缓存
    • Cache<Object, AuthorizationInfo> authorizationCache:授权缓存
    • PermissionResolver:权限解析器
    • RolePermissionResolver:角色权限解析器
  • AuthenticatingRealm
    • CredentialsMatcher:密码匹配器
    • boolean authenticationCachingEnabled:是否进行认证缓存
    • Cache<Object, AuthenticationInfo> authenticationCache:认证缓存
  • CachingRealm
    • CacheManager:实现缓存功能

③:Account:账号

在这里插入图片描述

  • SimpleAccount
    • SimpleAuthenticationInfo:认证信息
      • PrincipalCollection principals:凭证(用户名)
      • credentials:密码
      • ByteSource credentialsSalt:盐
    • SimpleAuthorizationInfo:授权信息
      • Set<String> roles:角色
      • Set<String> stringPermissions
      • Set<Permission> objectPermissions

3、解析

3.1、加载、解析阶段

  1. new IniRealm("classpath:shiro.ini")
    • IniRealm 调用类 Ini#load(Scanner) 加载并解析 shiro.ini 文件,解析结果存放属性 Map<String, Ini.Section> sections
    • 处理节点 users/roles:构造类 SimpleAccount(属性:认证:SimpleAuthenticationInfo authcInfo、授权:SimpleAuthorizationInfo authzInfo),并将处理结果存放在类 SimpleAccountRealm 的属性:Map<String, SimpleAccount> users、Map<String, SimpleRole> roles

IniRealm 构造器

在这里插入图片描述

①:Ini.fromResourcePath(resourcePath):通过类 Ini 进行解析、构造 Ini 对象

在这里插入图片描述

load(Scanner):最终调用这个方法进行解析

在这里插入图片描述

此方法的逻辑是:

  1. 先判断是否为注释(#; 符号开头):如果是,直接跳过;否则,进行解析。
  2. ini 文件格式:内容头、内容体。如果是内容头,则调用 addSection() 方法添加节点;否则,直接追加内容体

addSection() 方法:如果内容体不为空,则 Map<String, Ini.Section> sections 属性中添加新的节点

在这里插入图片描述

②:processDefinitions(Ini ini):解析 Map<String, Ini.Section> sections 属性中的节点,主要是 usersroles 节点

在这里插入图片描述

1、processRoleDefinitions() 方法:解析角色,构造 SimpleRole 对象,并添加到 SimpleAccountRealmMap<String, SimpleRole> roles

在这里插入图片描述

1-1、SimpleAccountRealm#add() 方法:将 SimpleRole 放入 roles

在这里插入图片描述

1-2 PermissionUtils.resolveDelimitedPermissions() 方法:通过 PermissionResolver 解析 permissions

在这里插入图片描述

【说明】在 ini 文件中,一个角色可以配置多个权限操作,通过 ","连接。如:admin=user:delete:1,order:query:*

1-2-1 resolvePermission():直接调用了 WildcardPermission 的构造器并返回

在这里插入图片描述

1-2-1-1 WildcardPermission#setParts() 方法:

在这里插入图片描述

2、processUserDefinitions() 方法:解析用户,构造 SimpleAccount 对象,并添加到 SimpleAccountRealmMap<String, SimpleAccount> users

在这里插入图片描述

由以上代码知:也可以只输入密码,不用添加角色

[users]
#用户名=密码
christy=123456

2-1、new SimpleAccount()SimpleAccount 构造器

在这里插入图片描述

2-1-1、new SimplePrincipalCollection():将当前 realm 名称与凭证(用户名)存入 Map<String, Set> realmPrincipals 属性中

在这里插入图片描述

2-1-1-1、add() 方法:

在这里插入图片描述

  1. setRealm(Realm realm):给 SecurityManager、认证器、授权器设置 realm

    • 给类 ModularRealmAuthenticator 设置 realms
    • 给类 ModularRealmAuthorizer 设置 realms

在这里插入图片描述

RealmSecurityManager#afterRealmsSet() 方法:被子类 AuthenticatingSecurityManagerAuthorizingSecurityManager 重写

在这里插入图片描述

AuthorizingSecurityManager#afterRealmsSet() 方法又去调用父类 AuthenticatingSecurityManager#afterRealmsSet() 方法:

  1. SecurityUtils.getSubject():通过类 ThreadContext 的属性 ThreadLocal<Map<Object, Object>> resources 获取与当前线程绑定的 Subject(Subject 来源于 DefaultSecurityManager.createSubject() 方法:最终通过 DefaultSubjectFactory 类进行 new DelegatingSubject())

1、getSubject() 方法:通过 ThreadContext 获取,如果获取成功,则直接返回;否则,先创建 Subejct,再绑定,最后返回

在这里插入图片描述
1-1、ThreadContext#getSubject() 方法:最终通过属性 ThreadLocal<Map<Object, Object>> resources 中获取,key 为 ThreadContext.class.getName() + "_SUBJECT_KEY"

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

ThreadLocal<Map<Object, Object>> resources:是与当前线程绑定的

1-2、new Builder()Builder 是接口 Subject 的一个内部类

在这里插入图片描述

1-2-1、newSubjectContextInstance() 方法:创建 DefaultSubjectContext 对象

在这里插入图片描述

1-3、setSecurityManager() 方法:设置 SecurityManager

在这里插入图片描述

1-3-1、put() 方法:最终往属性 Map<String, Object> backingMap 中 put

在这里插入图片描述

DefaultSubjectContext:是一个 Map 结构

在这里插入图片描述

1-4、buildSubject() 方法:创建 Subject。通过 SecurityManager 创建

在这里插入图片描述

1-4-1、createSubject() 方法:

  • 复制了一份 SubjectContext
  • 验证 SubjectContext 是否有 SecurityManager
  • 解析 Session。从属性 backingMap 获取。默认为 null
  • 解析 凭证
  • 创建 Subject。通过工厂创建 Subject
  • 保存 Subject 的凭证、session

在这里插入图片描述

1-4-1-1、resolveSession() 方法

  • 先从 SubjectContext 中获取 Session。默认为 null
  • 再从 Session 中获取

在这里插入图片描述
在这里插入图片描述

1-4-1-2、resolvePrincipals() 方法:

  • 先从 SubjectContext 中获取 凭证。默认为 null
  • 再从 RememberMeManager 获取
    在这里插入图片描述

1-4-1-3、createSubject() 方法:直接 new 了一个 DelegatingSubject

在这里插入图片描述

1-4-1-4、save() 方法:通过 subjectDAO 去操作

在这里插入图片描述

save() 方法:

在这里插入图片描述

3.2、认证阶段

  1. subject.login(token)
    • Subject 先委托给类 DefaultSecurityManager 进行认证
    • 再由类 AuthenticatingSecurityManager 委托给类 AbstractAuthenticator 进行认证(实际是它的子类 ModularRealmAuthenticator 进行认证)
    • 最终通过类 SimpleAccountRealm#doGetAuthenticationInfo() 进行用户名认证;通过类 CredentialsMatcher 进行密码认证

DelegatingSubject#login():委托给 SecurityManager 去认证

在这里插入图片描述

1、DefaultSecurityManager#login()

  • 通过调用父类 AuthenticatingSecurityManager#authenticate() 方法 去实现认证
  • 认证成功后,去创建 subject
  • 【记住我】逻辑处理

在这里插入图片描述

1-1、AuthenticatingSecurityManager#authenticate() 方法:通过 Authenticator 去认证

在这里插入图片描述

1-1-1、AbstractAuthenticator#authenticate() 方法:进行认证

  • 如果认证成功,如果有监听器 AuthenticationListener,则执行成功后的动作,并返回 AuthenticationInfo 信息
  • 如果认证失败(info == null),则抛异常

在这里插入图片描述

【注意】:如果返回的 info 结果为空,则 throw new AuthenticationException(msg);

1-1-1-1、ModularRealmAuthenticator#doAuthenticate() 方法:通过其子类去认证

  • 如果只配置一个 realm,则调用 doSingleRealmAuthentication() 方法;否则,调用 doMultiRealmAuthentication() 方法

在这里插入图片描述

1-1-1-1-1、doSingleRealmAuthentication() 方法:

  • 先判断 realm 是否支持当前的 token(类型:UsernamePasswordToken?还是自定义类型?)
  • 根据 token 从 realm 中获取 info,如果 info == null,则抛异常

在这里插入图片描述

1-1-1-1-1-1、supports() 方法:判断当前的 realm 是否支持 token

这里:判断 token 类型是否为 AuthenticationToken 类型(默认为 UsernamePasswordToken

在这里插入图片描述
在这里插入图片描述

token 不为空,且 AuthenticatingRealm 有一个属性 authenticationTokenClass,它的类型要和 token 一直才返回 true;否则,返回 false

在这里插入图片描述

在初始化过程中,authenticationTokenClass 已经被赋值为了 UsernamePasswordToken 类型。所以,如果 token 类型为 UsernamePasswordToken,返回 true;否则,返回 false

1-1-1-1-1-2、getAuthenticationInfo() 方法:获取认证信息

  • 先从缓存获取
  • 如果缓存有,则通过 CredentialsMatcher 直接进行密码匹配,如果匹配成功,则直接返回 info;否则,则抛出异常
  • 如果缓存没有,则从 realm 中拿(默认是从 SimpleAccountRealm 中拿取,ini 文件中配置的账号、角色存储在这个里面),再放入缓存中,进行密码匹配

在这里插入图片描述

1-1-1-1-1-2-1、getCachedAuthenticationInfo() 方法:从缓存中获取

在这里插入图片描述

1-1-1-1-1-2-1-1 getAvailableAuthenticationCache() 方法:拿取可用的 cache

  • 先获取 cache,默认为 null
  • 判断是否需要缓存,如果需要,且没有 cache,则从 CacheManager 中拿

在这里插入图片描述

1-1-1-1-1-2-1-1-1 getAuthenticationCacheLazy() 方法:从 CacheManager 中拿去 cache

在这里插入图片描述

1-1-1-1-1-2-2、doGetAuthenticationInfo() 方法:从 realm 中获取认证信息

  • doGetAuthenticationInfo() 是个抽象方法,留给子类实现。
  • 这里默认的实现是 SimpleAccountRealm,通过用户名获取 SimpleAccount

在这里插入图片描述

1-1-1-1-1-2-3、cacheAuthenticationInfoIfPossible() 方法:如果允许进行缓存,则缓存信息

在这里插入图片描述

1-1-1-1-1-2-4、assertCredentialsMatch() 方法:通过 CredentialsMatcher 进行密码认证

在这里插入图片描述

1-2、createSubject() 方法:创建 Subject

最终流程:

  1. 创建 SecurityManager:SecurityManager 是用来提供安全服务的,所以在做 Shiro 认证的时候要先创建此对象
  2. 主体 Subject 提交请求给 SecurityManager
  3. SecurityManager 调用 Authenticator 组件做认证
  4. Authenticator 通过 Realm 来从数据源中获取认证数据

3.3、授权阶段

  1. subject.hasRole("admin"):判断是否有角色

DelegatingSubject#hasRole() 调用 SecurityManager#hasRole() 进行认证:

在这里插入图片描述

AuthorizingSecurityManager 又调用 ModularRealmAuthorizer#hasRole()

在这里插入图片描述

ModularRealmAuthorizer 又调用 AuthorizingRealm#hasRole()

在这里插入图片描述
在这里插入图片描述

AuthorizingRealm#doGetAuthorizationInfo(PrincipalCollection) 是个抽象方法,由子类实现:

在这里插入图片描述
在这里插入图片描述


protected boolean hasRole(String roleIdentifier, AuthorizationInfo info) {return info != null && info.getRoles() != null && info.getRoles().contains(roleIdentifier);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1527618.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

AIGC简化文件管理:Python自动重命名Word和PDF文件

1.背景 大家应该也有遇到&#xff0c;自己电脑有很多文件命名不合理的文件&#xff0c;比如&#xff1a;文件1、想法3 &#xff0c;当你长时间再看到这个文件的时候&#xff0c;已经很难知道文件内容。 今天我们将借助AIGC的编码能力&#xff0c;帮我们生成一个批量改文件名的…

营养作用的对象是有区别的 第八篇

除了7大营养素 还需要补充其他营养素 食品营养学 临床营养学 大众营养学 食品营养学 你要早点就开始预防

【超级干货】2天速成PyTorch深度学习入门教程,缓解研究生焦虑

3、cnn基础 卷积神经网络 输入层 —输入图片矩阵 输入层一般是 RGB 图像或单通道的灰度图像,图片像素值在[0,255],可以用矩阵表示图片卷积层 —特征提取 人通过特征进行图像识别,根据左图直的笔画判断X,右图曲的笔画判断圆 卷积操作 激活层 —加强特征 池化层 —压缩数据…

qt多线程的两种方法run和movetothread

qt多线程的有什么用&#xff1f; 将耗时长的操作丢入专属线程执行&#xff0c;这样就不会影响主线程的界面操作&#xff0c;操作完再用信号槽等的方式返回结果 1.界面和部件相关都必须在主界面运行&#xff0c;不要用子线程调用或者操作&#xff0c;会引起奇怪的bug&#xff…

【Python 千题 —— 算法篇】字符统计

Python 千题持续更新中 …… 脑图地址 &#x1f449;&#xff1a;⭐https://twilight-fanyi.gitee.io/mind-map/Python千题.html⭐ 题目背景 在编程中&#xff0c;对字符串的字符统计是一个常见任务。这在文本处理、数据分析、词频统计、自然语言处理等领域有广泛应用。无论…

AI周报(9.1-9.7)

AI应用-Tidal 引领海洋养殖革命 Tidal团队&#xff0c;一个源自Alphabet X的创新项目&#xff0c;今年七月顺利从X实验室毕业&#xff0c;成为一家独立的公司。Tidal正在通过人工智能技术改变海洋养殖&#xff0c;特别是鲑鱼养殖。Tidal的总部位于挪威特隆赫姆&#xff0c;他们…

微信小程序和公众号的区别

微信小程序和公众号都是基于微信平台的两种不同应用形态&#xff0c;它们在展现形式、主要功能以及技术要求等方面存在显著区别。具体分析如下&#xff1a; 微信小程序和公众号的区别 展现形式 小程序&#xff1a;类似于APP的应用体验&#xff0c;用户可以通过扫一扫或者搜索…

数据结构——单链表实现和注释浅解

关于单链表的基础部分增删查改的实现和一点理解&#xff0c;写在注释里~ SList.h #pragma once #include<stdio.h> #include<stdlib.h> #include<assert.h>//定义节点的结构 //数据 指向下一个节点的指针 typedef int SLTDataType;typedef struct SListNo…

如何限制与管控员工上网行为?五个管控方法让员工效率倍增!

在现代企业中&#xff0c;互联网是工作中不可或缺的工具&#xff0c;但与此同时&#xff0c;员工在工作时间浏览与工作无关的网站、进行网络娱乐等行为&#xff0c;也成为了影响企业生产力和效率的主要因素之一。如何有效限制和管控员工的上网行为&#xff0c;从而提升工作效率…

解决ubuntu系统无法与FinalShell无法连接问题

问题 解决方案 先下载ubuntu网络工具 sudo apt install net-tools输入密码 下载完成后进入管理员模式查看密码 sudo -ihostname -I查看IP 得到ip地址后再继续安装 openssh-server 插件 sudo apt-get install openssh-server问题解决 尝试连接FinalShell

跨系统环境下LabVIEW程序稳定运行

在LabVIEW开发中&#xff0c;不同电脑的配置和操作系统&#xff08;如Win11与Win7&#xff09;可能对程序的稳定运行产生影响。为了确保程序在不同平台上都能正常且稳定运行&#xff0c;需要从兼容性、驱动、以及性能优化等多个方面入手。本文将详细介绍如何在不同系统环境下&a…

每日OJ_牛客_骆驼命名法(递归深搜)

目录 牛客_骆驼命名法&#xff08;简单模拟&#xff09; 解析代码 牛客_骆驼命名法&#xff08;简单模拟&#xff09; 骆驼命名法__牛客网 解析代码 首先一个字符一个字符的读取内容&#xff1a; 遇到 _ 就直接跳过。如果上一个字符是 _ 则下一个字符转大写字母。 #inclu…

从0开始深度学习(4)——线性回归概念

1 线性回归 回归&#xff08;regression&#xff09;指能为一个或多个自变量与因变量之间的关系进行建模。 1.1 线性模型 线性假设是指目标可以表示为特征的加权和&#xff0c;以房价和面积、房龄为例&#xff0c;可以有下面的式子&#xff1a; w称为权重&#xff08;weigh…

Centos7.9部署Gitlab-ce-16.9

一、环境信息 软件/系统名称版本下载地址备注Centos77.9.2009https://mirrors.nju.edu.cn/centos/7.9.2009/isos/x86_64/CentOS-7-x86_64-DVD-2009.isogitlab-cegitlab-ce-16.9.1https://mirror.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-16.9.1-ce.0.el7.x86_64.rpm…

磁电偶极子天线学习1 一种60GHz 宽带圆极化口径耦合磁电偶极子天线阵列

摘要&#xff1a; 一种新型的圆极化口径耦合天线被提出。这种圆极化磁电偶极子天线由刻蚀在短路基片集成波导的一部分的宽臂上&#xff0c;并且很容易被集成基片。在工作频段内实现了宽于28.8%的阻抗带宽和宽带3-dB的25.9%的轴比和的增益。此外&#xff0c;因为圆极化辐射由两个…

win11如何录屏

在 Win11 中录屏可以使用系统自带的工具和一些第三方应用。以下是几种方法&#xff1a; 方法一&#xff1a;使用 Xbox Game Bar 1. 打开 Xbox Game Bar - 按 Win G 组合键打开 Xbox Game Bar。 2. 开始录制 - 在显示的界面中&#xff0c;点击“录制”按钮&#xff08;…

C++入门(06)安装QT并快速测试体验一个简单的C++GUI项目

文章目录 1. 清华镜像源下载2. 安装3. 开始菜单上的 QT 工具4. 打开 Qt Creator5. 简单的 GUI C 项目5.1 打开 Qt Creator 并创建新项目5.2 设计界面5.3 添加按钮的点击事件5.4 编译并运行项目 6. 信号和槽&#xff08;Signals and Slots&#xff09; 这里用到了C类与对象的很多…

知名AIGC人工智能专家培训讲师唐兴通谈AI大模型数字化转型数字新媒体营销与数字化销售

在过去的二十年里&#xff0c;中国企业在数字营销领域经历了一场惊心动魄的变革。从最初的懵懂无知到如今的游刃有余&#xff0c;这一路走来&#xff0c;既有模仿学习的艰辛&#xff0c;也有创新突破的喜悦。然而&#xff0c;站在人工智能时代的门槛上&#xff0c;我们不禁要问…

认知杂谈53

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

【Map】、集合总结

Map(*)——映射 比较之前的集合 List 为什么使用map <k,v>&#xff1a;key–value Api–>尽量用k去操作value put<k,v> package com.ffyc.map;import java.util.HashMap; import java.util.Map;/*** 映射*/ public class MapDemo {public static void main(St…