【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

相关文章

Java程序进程起来了但是不打印日志的原因分析

《Java程序进程起来了但是不打印日志的原因分析》:本文主要介绍Java程序进程起来了但是不打印日志的原因分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java程序进程起来了但是不打印日志的原因1、日志配置问题2、日志文件权限问题3、日志文件路径问题4、程序

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注

Python虚拟环境终极(含PyCharm的使用教程)

《Python虚拟环境终极(含PyCharm的使用教程)》:本文主要介绍Python虚拟环境终极(含PyCharm的使用教程),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录一、为什么需要虚拟环境?二、虚拟环境创建方式对比三、命令行创建虚拟环境(venv)3.1 基础命令3

使用Node.js制作图片上传服务的详细教程

《使用Node.js制作图片上传服务的详细教程》在现代Web应用开发中,图片上传是一项常见且重要的功能,借助Node.js强大的生态系统,我们可以轻松搭建高效的图片上传服务,本文将深入探讨如何使用No... 目录准备工作搭建 Express 服务器配置 multer 进行图片上传处理图片上传请求完整代码示例

SpringSecurity JWT基于令牌的无状态认证实现

《SpringSecurityJWT基于令牌的无状态认证实现》SpringSecurity中实现基于JWT的无状态认证是一种常见的做法,本文就来介绍一下SpringSecurityJWT基于令牌的无... 目录引言一、JWT基本原理与结构二、Spring Security JWT依赖配置三、JWT令牌生成与

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

python连接本地SQL server详细图文教程

《python连接本地SQLserver详细图文教程》在数据分析领域,经常需要从数据库中获取数据进行分析和处理,下面:本文主要介绍python连接本地SQLserver的相关资料,文中通过代码... 目录一.设置本地账号1.新建用户2.开启双重验证3,开启TCP/IP本地服务二js.python连接实例1.