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

2024-09-08 11:52

本文主要是介绍【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);
}

这篇关于【Shiro】Shiro 的学习教程(二)之认证、授权源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1148043

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]