Apache Shiro 教程
2026/4/1 16:18:10 网站建设 项目流程

Apache Shiro 完整教程

📚教程目标:通过理论学习和代码实践,全面掌握Apache Shiro的核心功能、架构设计和最佳实践,能够在实际项目中灵活应用Shiro实现安全认证和授权。


📖 学习路径导航

章节结构

  1. 第一章:Shiro概述与基础架构- 了解Shiro是什么、核心架构和设计理念
  2. 第二章:身份认证(Authentication)- 学习用户登录认证的实现
  3. 第三章:角色管理(Roles)- 掌握基于角色的授权机制
  4. 第四章:权限控制(Permissions)- 深入学习细粒度权限控制
  5. 第五章:会话管理(Session Management)- 了解Shiro的会话管理功能
  6. 第六章:密码加密(Encryption)- 学习安全的密码加密技术
  7. 第七章:自定义Realm- 掌握自定义数据源连接
  8. 第八章:缓存支持(Cache)- 了解如何优化Shiro性能
  9. 第九章:JWT集成- 学习现代无状态认证方式

🎯 教程核心价值

通过本教程的学习,你将能够:

掌握Shiro核心概念- 理解认证、授权、会话等基础理论
熟悉架构设计- 了解SecurityManager、Subject、Realm等核心组件
具备实践能力- 能够编写完整的Shiro应用
解决实际问题- 应对各种安全场景
做出技术选型- 根据项目需求选择合适的安全方案


第一章:Shiro概述与基础架构

🎯 学习目标

  • 理解Apache Shiro是什么
  • 了解Shiro的发展历史和设计理念
  • 掌握Shiro的核心架构和组件
  • 明确Shiro在现代应用中的定位
  • 理解Shiro的核心优势

🔍 什么是Apache Shiro?

Apache Shiro是一个功能强大且易于使用的Java安全框架,提供了认证、授权、会话管理和加密等功能,用于保护任何类型的应用程序 - 从命令行工具到大型企业级Web应用。

📚 设计理念

“简单即美”- Shiro的设计哲学是让复杂的安全操作变得简单直观

🌟 Shiro核心优势

  1. 简洁直观的API设计

    • 最少知识原则:开发者只需要了解必要的API
    • 约定优于配置:合理的默认配置,减少配置负担
    • 分层抽象:不同层次的抽象满足不同复杂度的需求
  2. 全面的安全功能覆盖

    • 认证:多因素认证、JWT
    • 授权:基于角色、权限、资源的细粒度控制
    • 会话:支持Web和非Web环境、分布式会话
    • 加密:支持MD5、SHA、AES等主流加密算法
  3. 灵活的架构设计

    • 模块化架构:插件式Realm,轻松切换和组合不同的数据源
    • 策略模式:认证、授权策略可配置
    • 事件驱动:支持安全事件的监听和处理
    • 注解支持:基于注解的声明式安全控制
  4. 优秀的性能表现

    • 智能缓存:认证和授权结果的智能缓存
    • 懒加载:安全对象的按需加载
    • 连接池:数据库连接的有效管理
    • 最小化锁:并发场景下的性能优化

🏗️ 核心架构

数据层 - Data Layer
核心层 - Core Layer
应用层 - Application Layer
Realm 数据源连接器
JDBC Realm 数据库
LDAP Realm 目录服务
Custom Realm 自定义数据源
Text Realm 文本配置
Authenticator 认证器
SecurityManager 安全管理器
Authorizer 授权器
SessionManager 会话管理器
CacheManager 缓存管理器
AuthenticationStrategy 认证策略
CredentialMatcher 凭证匹配器
PermissionResolver 权限解析器
RolePermissionResolver 角色权限解析器
SessionDAO 会话存储
SessionFactory 会话工厂
SessionValidationScheduler 会话验证调度器
SecurityUtils 安全工具
Subject 当前用户

🔄 核心组件交互流程

应用程序SubjectSecurityManagerRealm数据源创建认证请求委托认证处理查询安全数据获取用户凭证返回用户数据返回认证结果认证成功/失败返回认证状态同时处理授权、会话等应用程序SubjectSecurityManagerRealm数据源

🎭 核心组件详解

组件角色核心职责代码示例
Subject🎭 当前用户代表与系统交互的实体SecurityUtils.getSubject()
SecurityManager🏰 安全管家协调所有安全操作new DefaultSecurityManager()
Realm🗄️ 数据桥梁连接安全数据源new IniRealm("classpath:shiro.ini")
Authenticator🔍 身份验证官验证用户身份ModularRealmAuthenticator
Authorizer⚖️ 权限审核官检查用户权限ModularRealmAuthorizer
SessionManager📋 会话管理员管理用户会话DefaultSessionManager
CacheManager⚡ 性能加速器缓存安全数据MemoryConstrainedCacheManager

🔍 Shiro 与 Spring Security 对比

功能特性Apache ShiroSpring SecurityShiro优势
学习曲线平缓陡峭API更简洁直观
配置复杂度简单复杂配置更直观
独立运行支持依赖Spring可独立使用
Web支持完善完善同样强大
非Web支持优秀一般更好的通用性
缓存机制内置需集成缓存更完善
会话管理强大一般会话功能更丰富
加密功能完善需集成加密功能更完整
授权灵活性极高平衡灵活与简洁
社区活跃度稳定活跃成熟稳定
选择建议

选择Shiro的场景

  • 需要独立运行的安全框架
  • 项目不基于Spring框架
  • 需要丰富的会话管理功能
  • 追求简洁的API和配置
  • 需要非Web环境的安全支持

选择Spring Security的场景

  • 项目已基于Spring框架
  • 需要与Spring生态深度集成
  • 团队已熟悉Spring技术栈
  • 需要更细粒度的安全控制

🏆 学习成果检验

✅ 基础理解
  • 能够解释Shiro的核心概念
  • 能够描述Shiro的三层架构
  • 能够说出核心组件的作用
  • 能够列举Shiro的核心优势

第二章:身份认证(Authentication)

🎯 学习目标

  • 理解身份认证的概念
  • 掌握Shiro认证流程
  • 学习如何配置用户账户
  • 掌握异常处理方法

🔐 认证流程详解

认证架构设计
flowchart TD A[用户提交认证请求] --> B[Subject.login token] B --> C[SecurityManager 接收请求] C --> D[Authenticator 开始认证] D --> E[遍历配置 的Realms] E --> F{Realm支持 此token?} F -->|是| G[Realm执行 doGetAuthenticationInfo] F -->|否| H[跳过此Realm] G --> I{认证 成功?} I -->|是| J[返回 AuthenticationInfo] I -->|否| K[抛出 AuthenticationException] H --> E J --> L[创建 Subject身份] K --> M[认证失败 处理] L --> N[认证成功 登录状态] style A fill:#e1f5fe style N fill:#c8e6c9 style M fill:#ffcdd2 style D fill:#e8f5e8

💡 常见认证异常

异常类型说明处理方式
UnknownAccountException用户名不存在提示用户注册或检查用户名
IncorrectCredentialsException密码错误提示用户重新输入密码
LockedAccountException账户已锁定联系管理员解锁账户
DisabledAccountException账户已禁用联系管理员启用账户
AuthenticationException其他认证异常通用异常处理

💻 代码示例

项目代码文件src/main/java/com/shiro/tutorial/SimplifiedAuthExample.java

packagecom.shiro.tutorial;importorg.apache.shiro.SecurityUtils;importorg.apache.shiro.authc.AuthenticationException;importorg.apache.shiro.authc.IncorrectCredentialsException;importorg.apache.shiro.authc.UnknownAccountException;importorg.apache.shiro.authc.UsernamePasswordToken;importorg.apache.shiro.mgt.DefaultSecurityManager;importorg.apache.shiro.realm.text.IniRealm;importorg.apache.shiro.subject.Subject;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;/** * Apache Shiro 简单身份认证示例类 * 开发思路: * 1. 首先配置安全管理器和领域(Realm) * 2. 获取当前用户主体(Subject) * 3. 创建身份令牌进行用户认证 * 4. 处理各种认证异常情况 * * @author 李昊哲 李胜龙 * @version 1.0.0 * @since 2025-12-15 */publicclassSimplifiedAuthExample{// 创建日志记录器,用于输出程序运行信息privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(SimplifiedAuthExample.class);/** * 程序入口方法 * 开发过程: * 1. 初始化Shiro安全框架组件 * 2. 进行用户身份认证 * 3. 处理认证结果和异常情况 * * @param args 命令行参数 */publicstaticvoidmain(String[]args){/* 当前示例是如何工作的 1. 在 SimplifiedAuthExample.java 中,认证流程如下: 2. 首先检查用户是否已经认证过(通常在首次运行时返回 false) 3. 如果未认证,提示用户输入用户名和密码 4. 创建 UsernamePasswordToken 对象封装用户输入的凭据 5. 调用 currentUser.login(token) 进行认证 Shiro 在后台使用 IniRealm 读取 shiro.ini 文件中的用户信息,并与用户输入的凭据进行比较 所以,系统是拿着用户输入的账号密码去数据源中验证的。shiro.ini 文件就是我们的"数据源",虽然在实际项目中,我们会使用数据库或 LDAP 等更复杂的存储系统。 */// 创建 IniRealm 实例,指定配置文件位置为classpath下的shiro.ini// IniRealm是Shiro提供的基于INI配置文件的身份认证和授权实现IniRealminiRealm=newIniRealm("classpath:shiro.ini");// 创建 SecurityManager 安全管理器实例,并传入realm// SecurityManager是Shiro框架的核心,负责协调内部安全组件DefaultSecurityManagersecurityManager=newDefaultSecurityManager(iniRealm);// 将 SecurityManager 绑定到 SecurityUtils 工具类// 这样在整个应用程序中都可以通过SecurityUtils获取SecurityManagerSecurityUtils.setSecurityManager(securityManager);// 获取当前执行用户(Subject),Subject代表当前正在与软件进行交互的用户SubjectcurrentUser=SecurityUtils.getSubject();// 判断当前用户是否已经通过认证if(!currentUser.isAuthenticated()){// 如果未认证,则创建UsernamePasswordToken令牌,用于封装用户身份信息// 这里使用预设的用户名"user"和密码"password"UsernamePasswordTokentoken=newUsernamePasswordToken("user","password");try{// 使用令牌进行用户登录认证currentUser.login(token);// 认证成功后,记录用户登录成功的日志信息LOGGER.info("用户 {} 登录成功!",currentUser.getPrincipal());}catch(UnknownAccountExceptionuae){// 捕获用户名不存在异常LOGGER.error("用户名不存在",uae);}catch(IncorrectCredentialsExceptionice){// 捕获密码错误异常LOGGER.error("密码错误",ice);}catch(AuthenticationExceptionae){// 捕获其他身份认证相关异常LOGGER.error("认证失败",ae);}}else{// 如果用户已经通过认证,则记录相应日志LOGGER.info("用户已认证");}}}

� 代码执行逻辑

UnknownAccountException
IncorrectCredentialsException
其他
创建IniRealm实例
创建DefaultSecurityManager实例
绑定到SecurityUtils
获取Subject
是否已认证?
记录已认证日志
创建UsernamePasswordToken
尝试登录
登录成功?
记录登录成功日志
异常类型?
记录用户名不存在
记录密码错误
记录认证失败
退出程序

� 配置文件

项目配置文件src/main/resources/shiro.ini

# 用户配置部分,定义系统中的用户及其密码 [users] # 格式: 用户名 = 密码 user = password

🏆 学习成果检验

✅ 实践任务
  1. 运行SimplifiedAuthExample类,观察输出结果
  2. 修改用户名或密码,观察异常情况
  3. 尝试添加新用户到配置文件

第三章:角色管理(Roles)

🎯 学习目标

  • 理解角色的概念
  • 掌握角色分配方法
  • 学习角色检查机制
  • 了解角色与权限的关系

🧩 角色授权模型

RBAC模型深度解析
USERbigintidPKstringusernamestringpasswordUSER_ROLEbigintuser_idFKbigintrole_idFKROLEbigintidPKstringnamestringdescriptionROLE_PERMISSIONbigintrole_idFKbigintpermission_idFKPERMISSIONbigintidPKstringpermission分配到包含用户拥有权限分配给角色

🎯 角色检查方法

方法说明返回值
hasRole(String role)检查用户是否具有指定角色boolean
hasRoles(List<String> roles)检查用户是否具有多个角色boolean[]
hasAllRoles(Collection<String> roles)检查用户是否具有所有指定角色boolean
checkRole(String role)检查用户是否具有指定角色,失败抛出异常void

💻 代码示例

项目代码文件src/main/java/com/shiro/tutorial/RoleExample.java

packagecom.shiro.tutorial;importorg.apache.shiro.SecurityUtils;importorg.apache.shiro.authc.UsernamePasswordToken;importorg.apache.shiro.mgt.DefaultSecurityManager;importorg.apache.shiro.realm.text.IniRealm;importorg.apache.shiro.subject.Subject;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;/** * Apache Shiro 角色权限控制示例类 * 开发思路: * 1. 首先配置安全管理器和领域(Realm) * 2. 获取当前用户主体(Subject) * 3. 创建身份令牌进行用户认证 * 4. 验证用户角色 * * @author 李昊哲 李胜龙 * @version 1.0.0 * @since 2025-12-15 */publicclassRoleExample{// 创建日志记录器,用于输出程序运行信息privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(RoleExample.class);/** * 程序入口方法 * 开发过程: * 1. 初始化Shiro安全框架组件,加载角色配置 * 2. 进行用户身份认证 * 3. 验证用户角色权限检查功能 * * @param args 命令行参数 */publicstaticvoidmain(String[]args){// 创建 IniRealm 实例,使用专门的角色配置文件shiro-role.ini// 该配置文件中包含了用户角色相关信息IniRealminiRealm=newIniRealm("classpath:shiro-role.ini");// 创建 SecurityManager 安全管理器实例,并传入realm// SecurityManager是Shiro框架的核心,负责协调内部安全组件DefaultSecurityManagersecurityManager=newDefaultSecurityManager(iniRealm);// 将 SecurityManager 绑定到 SecurityUtils 工具类// 这样在整个应用程序中都可以通过SecurityUtils获取SecurityManagerSecurityUtils.setSecurityManager(securityManager);// 获取当前执行用户(Subject),Subject代表当前正在与软件进行交互的用户SubjectcurrentUser=SecurityUtils.getSubject();// 判断当前用户是否已经通过认证if(!currentUser.isAuthenticated()){// 如果未认证,则创建UsernamePasswordToken令牌,用于封装用户身份信息// 这里使用预设的用户名"user"和密码"password"UsernamePasswordTokentoken=newUsernamePasswordToken("user","password");// 使用令牌进行用户登录认证currentUser.login(token);// 认证成功后,记录用户登录成功的日志信息LOGGER.info("用户 {} 登录成功!",currentUser.getPrincipal());}// 检查用户是否具有"role"角色(与配置文件中定义的角色一致)// hasRole方法用于判断当前用户是否拥有指定角色if(currentUser.hasRole("role")){LOGGER.info("用户 {} 拥有角色 role",currentUser.getPrincipal());}else{LOGGER.info("用户 {} 没有角色 role",currentUser.getPrincipal());}// 检查用户是否具有"admin"角色(根据配置应该不存在此角色)if(currentUser.hasRole("admin")){LOGGER.info("用户 {} 拥有角色 admin",currentUser.getPrincipal());}else{LOGGER.info("用户 {} 没有角色 admin",currentUser.getPrincipal());}}}

🔄 代码执行逻辑

创建IniRealm实例
加载shiro-role.ini配置文件
创建DefaultSecurityManager实例
将IniRealm传入SecurityManager
将SecurityManager绑定到SecurityUtils
获取当前用户Subject
检查用户是否已认证
检查用户是否具有'role'角色
创建UsernamePasswordToken
使用token进行登录认证
记录用户登录成功日志
输出'用户拥有角色 role'日志
输出'用户没有角色 role'日志
检查用户是否具有'admin'角色
输出'用户拥有角色 admin'日志
输出'用户没有角色 admin'日志
程序结束

📝 配置文件

项目配置文件src/main/resources/shiro-role.ini

# 用户配置部分,用于定义系统中的用户、密码及其所属角色 [users] # 格式: 用户名 = 密码, 角色1, 角色2... # 示例配置了一个用户名为"user",密码为"password"的用户,该用户属于"role"角色 user = password, role # 角色权限配置部分,用于定义角色所拥有的权限 [roles] # 格式: 角色名 = 权限 # 示例配置了"role"角色拥有"permission"权限 role = permission

🏆 学习成果检验

✅ 实践任务
  1. 运行RoleExample类,观察角色检查结果
  2. 修改配置文件,为用户添加多个角色
  3. 尝试使用hasRoles()hasAllRoles()方法检查多个角色
  4. 使用checkRole()方法检查角色

第四章:权限控制(Permissions)

🎯 学习目标

  • 理解权限的概念和格式
  • 掌握细粒度权限控制
  • 学习权限表达式语法
  • 了解权限检查方法
  • 学习通配符权限

🎮 权限表达式语法详解

权限格式定义
权限格式:资源:操作:实例 示例: - resource:action:object # 允许对指定资源执行操作 - user:* # 允许对用户进行所有操作 - *:read # 允许读取所有资源
权限格式组成
部分含义示例通配符支持
资源受保护的资源类型resource, user, order支持*
操作允许的操作类型action, create, read支持*
实例资源的具体实例object, 1, user1支持*

🎯 权限检查方法

方法说明返回值
isPermitted(String permission)检查用户是否具有指定权限boolean
isPermittedAll(String... permissions)检查用户是否具有所有指定权限boolean
checkPermission(String permission)检查用户是否具有指定权限,失败抛出异常void

💻 代码示例

项目代码文件src/main/java/com/shiro/tutorial/PermissionExample.java

packagecom.shiro.tutorial;importorg.apache.shiro.SecurityUtils;importorg.apache.shiro.authc.UsernamePasswordToken;importorg.apache.shiro.mgt.DefaultSecurityManager;importorg.apache.shiro.realm.text.IniRealm;importorg.apache.shiro.subject.Subject;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;/** * Apache Shiro 权限控制示例类 * 开发思路: * 1. 配置支持权限的Realm和安全管理器 * 2. 实现用户身份认证 * 3. 验证用户细粒度权限控制功能 */publicclassPermissionExample{// 创建日志记录器,用于输出程序运行信息privatestaticfinalLoggerlog=LoggerFactory.getLogger(PermissionExample.class);/** * 程序入口方法 * 开发过程: * 1. 初始化Shiro安全框架组件,加载权限配置 * 2. 进行用户身份认证 * 3. 验证用户权限检查功能 */publicstaticvoidmain(String[]args){// 创建 IniRealm 实例,使用专门的权限配置文件shiro-permission.iniIniRealminiRealm=newIniRealm("classpath:shiro-permission.ini");// 创建 SecurityManager 安全管理器实例,并传入realmDefaultSecurityManagersecurityManager=newDefaultSecurityManager(iniRealm);// 将 SecurityManager 绑定到 SecurityUtils 工具类SecurityUtils.setSecurityManager(securityManager);// 获取当前执行用户(Subject)SubjectcurrentUser=SecurityUtils.getSubject();// 判断当前用户是否已经通过认证if(!currentUser.isAuthenticated()){// 如果未认证,则创建UsernamePasswordToken令牌UsernamePasswordTokentoken=newUsernamePasswordToken("user","password");// 执行用户登录认证currentUser.login(token);// 认证成功后,记录用户登录成功的日志信息log.info("用户 {} 登录成功!",currentUser.getPrincipal());}// 检查用户是否具有"resource:action:object"权限if(currentUser.isPermitted("resource:action:object")){log.info("用户有权访问 resource:action:object");}else{log.info("用户无权访问 resource:action:object");}// 检查用户是否具有"another:resource:action"权限if(currentUser.isPermitted("another:resource:action")){log.info("用户有权访问 another:resource:action");}else{log.info("用户无权访问 another:resource:action");}// 正常退出程序System.exit(0);}}

� 代码执行逻辑

创建IniRealm实例
创建DefaultSecurityManager实例
绑定到SecurityUtils
获取Subject
是否已认证?
跳过认证
创建UsernamePasswordToken
执行登录
记录登录成功日志
检查resource:action:object权限
检查another:resource:action权限
检查是否具有所有权限
使用checkPermission方法
退出程序

�� 配置文件

项目配置文件src/main/resources/shiro-permission.ini

# 用户配置,格式: 用户名 = 密码, 角色1, 角色2... [users] # 用户user具有role1和role2两个角色 user = password, role1, role2 # 用户admin具有admin角色 admin = admin123, admin # 角色权限配置,格式: 角色名 = 权限1, 权限2... [roles] # role1角色具有对resource的action权限,作用对象为object role1 = resource:action:object # role2角色具有对another的resource权限,作用对象为action role2 = another:resource:action # admin角色具有所有权限 admin = *

🏆 学习成果检验

✅ 实践任务
  1. 运行PermissionExample类,观察权限检查结果
  2. 修改配置文件,添加更多权限
  3. 尝试使用通配符权限
  4. 使用isPermittedAll()方法检查多个权限
  5. 使用checkPermission()方法检查权限

第五章:会话管理(Session Management)

🎯 学习目标

  • 理解Shiro会话的概念
  • 掌握会话操作方法
  • 学习会话属性管理
  • 了解会话生命周期

🌐 会话管理概述

Shiro提供了强大的会话管理功能,支持Web和非Web环境,具有以下特点:

  • 跨环境支持:相同的API在Web和非Web环境下均可使用
  • 丰富的会话操作:支持会话属性管理、超时设置等
  • 会话持久化:支持将会话存储到不同的数据源
  • 会话验证:自动验证会话的有效性

💻 代码示例

项目代码文件src/main/java/com/shiro/tutorial/SessionExample.java

packagecom.shiro.tutorial;importorg.apache.shiro.SecurityUtils;importorg.apache.shiro.authc.UsernamePasswordToken;importorg.apache.shiro.mgt.DefaultSecurityManager;importorg.apache.shiro.realm.text.IniRealm;importorg.apache.shiro.session.Session;importorg.apache.shiro.subject.Subject;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;publicclassSessionExample{privatestaticfinalLoggerlog=LoggerFactory.getLogger(SessionExample.class);publicstaticvoidmain(String[]args){// 创建 IniRealm 实例,使用专门的会话配置文件shiro-session.iniIniRealminiRealm=newIniRealm("classpath:shiro-session.ini");DefaultSecurityManagersecurityManager=newDefaultSecurityManager(iniRealm);SecurityUtils.setSecurityManager(securityManager);SubjectcurrentUser=SecurityUtils.getSubject();// 用户认证if(!currentUser.isAuthenticated()){// 注意:这里使用的是shiro-session.ini中配置的用户user1/password1UsernamePasswordTokentoken=newUsernamePasswordToken("user1","password1");currentUser.login(token);log.info("用户 {} 登录成功!",currentUser.getPrincipal());}// 获取当前用户的会话(Session)对象Sessionsession=currentUser.getSession();// 在会话中设置属性,存储用户相关信息session.setAttribute("username","user1");session.setAttribute("loginTime",System.currentTimeMillis());// 从会话中获取之前设置的属性值Stringusername=(String)session.getAttribute("username");LongloginTime=(Long)session.getAttribute("loginTime");// 输出从会话中获取的信息到日志log.info("从会话中获取用户名: {}",username);log.info("登录时间: {}",loginTime);// 从会话中移除指定属性session.removeAttribute("loginTime");log.info("loginTime 属性已被移除");System.exit(0);}}

� 代码执行逻辑

创建IniRealm实例
创建DefaultSecurityManager实例
绑定到SecurityUtils
获取Subject
是否已认证?
跳过认证
创建UsernamePasswordToken
执行登录
记录登录成功日志
获取Session对象
设置会话属性username
设置会话属性loginTime
获取会话属性username
获取会话属性loginTime
记录会话信息
移除会话属性loginTime
记录属性移除信息
退出程序

� 配置文件

项目配置文件src/main/resources/shiro-session.ini

# 用户配置,格式: 用户名 = 密码 [users] user1 = password1

🏆 学习成果检验

✅ 实践任务
  1. 运行SessionExample类,观察会话操作结果
  2. 尝试在会话中存储和获取更多属性
  3. 学习会话的生命周期管理

第六章:密码加密(Encryption)

🎯 学习目标

  • 理解密码加密的重要性
  • 掌握Shiro加密API的使用
  • 学习盐值加密技术
  • 了解不同加密算法的特点

🔒 密码加密概述

密码加密是保护用户数据安全的重要手段,Shiro提供了强大的加密支持:

  • 支持多种加密算法:MD5、SHA-1、SHA-256等
  • 内置盐值加密支持
  • 支持多次迭代加密

💻 代码示例

项目代码文件src/main/java/com/shiro/tutorial/EncryptionExample.java

packagecom.shiro.tutorial;importorg.apache.shiro.crypto.hash.SimpleHash;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;publicclassEncryptionExample{privatestaticfinalLoggerlog=LoggerFactory.getLogger(EncryptionExample.class);publicstaticvoidmain(String[]args){// 原始密码StringoriginalPassword="password";// 使用MD5算法加密密码Stringmd5Hash=newSimpleHash("MD5",originalPassword).toHex();log.info("原始密码: {}",originalPassword);log.info("MD5加密后: {}",md5Hash);// 使用SHA-256算法加密密码Stringsha256Hash=newSimpleHash("SHA-256",originalPassword).toHex();log.info("SHA-256加密后: {}",sha256Hash);// 使用盐值加密密码(增强安全性)Stringsalt="randomSalt123";StringsaltedHash=newSimpleHash("SHA-256",originalPassword,salt,1024).toHex();log.info("带盐值SHA-256加密后: {}",saltedHash);// 演示密码验证过程StringinputPassword="password";StringhashedInput=newSimpleHash("SHA-256",inputPassword,salt,1024).toHex();if(hashedInput.equals(saltedHash)){log.info("密码验证成功!");}else{log.info("密码验证失败!");}System.exit(0);}}

🔄 代码执行逻辑

定义原始密码
使用MD5加密
使用SHA-256加密
使用带盐值的SHA-256加密
输出MD5加密结果
输出SHA-256加密结果
输出带盐值SHA-256加密结果
使用正确密码验证
验证成功?
输出验证成功日志
输出验证失败日志
使用错误密码验证
验证成功?
输出错误密码验证成功日志
输出错误密码验证失败日志
退出程序

🏆 学习成果检验

✅ 实践任务
  1. 运行EncryptionExample类,观察不同加密算法的结果
  2. 尝试使用不同的盐值和迭代次数
  3. 理解盐值加密的重要性

第七章:自定义Realm

🎯 学习目标

  • 理解Realm的作用
  • 掌握自定义Realm的开发方法
  • 学习如何实现认证和授权逻辑
  • 了解多Realm配置

🗄️ 自定义Realm概述

Realm是Shiro与数据源之间的桥梁,负责从数据源获取安全数据。自定义Realm允许我们:

  • 从任意数据源获取数据
  • 实现自定义的认证逻辑
  • 实现自定义的授权逻辑

💻 代码示例

项目代码文件src/main/java/com/shiro/tutorial/CustomRealmExample.java

packagecom.shiro.tutorial;importorg.apache.shiro.SecurityUtils;importorg.apache.shiro.authc.*;importorg.apache.shiro.authz.AuthorizationInfo;importorg.apache.shiro.authz.SimpleAuthorizationInfo;importorg.apache.shiro.mgt.DefaultSecurityManager;importorg.apache.shiro.realm.AuthorizingRealm;importorg.apache.shiro.subject.PrincipalCollection;importorg.apache.shiro.subject.Subject;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importjava.util.HashMap;importjava.util.HashSet;importjava.util.Set;publicclassCustomRealmExample{privatestaticfinalLoggerlog=LoggerFactory.getLogger(CustomRealmExample.class);publicstaticvoidmain(String[]args){// 创建自定义 Realm 实例UserRealmuserRealm=newUserRealm();// 创建 SecurityManager 安全管理器实例,并传入自定义realmDefaultSecurityManagersecurityManager=newDefaultSecurityManager(userRealm);// 将 SecurityManager 绑定到 SecurityUtils 工具类SecurityUtils.setSecurityManager(securityManager);// 获取当前执行用户(Subject)SubjectcurrentUser=SecurityUtils.getSubject();// 判断当前用户是否已经通过认证if(!currentUser.isAuthenticated()){// 如果未认证,则创建UsernamePasswordToken令牌UsernamePasswordTokentoken=newUsernamePasswordToken("admin","admin123");try{// 使用令牌进行用户登录认证currentUser.login(token);// 认证成功后,记录用户登录成功的日志信息log.info("用户 {} 登录成功!",currentUser.getPrincipal());}catch(UnknownAccountExceptionuae){log.error("用户名不存在",uae);}catch(IncorrectCredentialsExceptionice){log.error("密码错误",ice);}catch(AuthenticationExceptionae){log.error("认证失败",ae);}}else{// 如果用户已经通过认证,则记录相应日志log.info("用户已认证");}// 验证用户是否有 admin 角色if(currentUser.hasRole("admin")){log.info("用户具有 admin 角色");}else{log.info("用户不具有 admin 角色");}// 验证用户是否有user:delete权限if(currentUser.isPermitted("user:delete")){log.info("用户有删除用户的权限");}else{log.info("用户没有删除用户的权限");}// 验证用户是否有product:create权限if(currentUser.isPermitted("product:create")){log.info("用户有创建产品的权限");}else{log.info("用户没有创建产品的权限");}System.exit(0);}/** * 自定义Realm类,用于处理用户认证和授权 */publicstaticclassUserRealmextendsAuthorizingRealm{// 模拟用户数据库privatestaticfinalHashMap<String,String>userDatabase=newHashMap<>();// 模拟角色数据库privatestaticfinalHashMap<String,Set<String>>roleDatabase=newHashMap<>();// 模拟权限数据库privatestaticfinalHashMap<String,Set<String>>permissionDatabase=newHashMap<>();// 初始化模拟数据static{// 添加用户(用户名:密码)userDatabase.put("admin","admin123");userDatabase.put("user","user123");// 添加用户角色Set<String>adminRoles=newHashSet<>();adminRoles.add("admin");adminRoles.add("user");roleDatabase.put("admin",adminRoles);Set<String>userRoles=newHashSet<>();userRoles.add("user");roleDatabase.put("user",userRoles);// 添加角色权限Set<String>adminPermissions=newHashSet<>();adminPermissions.add("user:*");adminPermissions.add("product:*");permissionDatabase.put("admin",adminPermissions);Set<String>userPermissions=newHashSet<>();userPermissions.add("product:view");userPermissions.add("product:create");permissionDatabase.put("user",userPermissions);}/** * 认证方法:获取认证信息 */@OverrideprotectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationTokenauthToken)throwsAuthenticationException{// 获取用户名Stringusername=(String)authToken.getPrincipal();// 从模拟数据库中获取用户密码Stringpassword=userDatabase.get(username);// 检查用户是否存在if(password==null){thrownewUnknownAccountException("用户不存在");}// 返回认证信息returnnewSimpleAuthenticationInfo(username,password,getName());}/** * 授权方法:获取授权信息 */@OverrideprotectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollectionprincipals){// 获取用户名Stringusername=(String)principals.getPrimaryPrincipal();// 创建授权信息对象SimpleAuthorizationInfoauthorizationInfo=newSimpleAuthorizationInfo();// 添加角色Set<String>roles=roleDatabase.get(username);if(roles!=null){authorizationInfo.setRoles(roles);}// 添加权限Set<String>permissions=permissionDatabase.get(username);if(permissions!=null){authorizationInfo.setStringPermissions(permissions);}returnauthorizationInfo;}}}

🔄 代码执行逻辑

创建自定义UserRealm实例
创建DefaultSecurityManager实例
绑定到SecurityUtils
获取Subject
是否已认证?
跳过认证
创建UsernamePasswordToken
执行登录
登录成功?
记录登录成功日志
记录认证失败日志
检查admin角色
角色检查成功?
记录角色检查通过日志
记录角色检查失败日志
检查user:delete权限
权限检查成功?
记录权限检查通过日志
记录权限检查失败日志
检查product:create权限
权限检查成功?
记录权限检查通过日志
记录权限检查失败日志
退出程序

🏆 学习成果检验

✅ 实践任务
  1. 运行CustomRealmExample类,观察自定义Realm的认证和授权结果
  2. 尝试修改模拟数据库中的用户、角色和权限信息
  3. 学习如何实现更复杂的自定义Realm

第八章:缓存支持(Cache)

🎯 学习目标

  • 理解Shiro缓存的概念和作用
  • 掌握Shiro缓存管理器的配置
  • 学习如何启用和配置认证缓存
  • 学习如何启用和配置授权缓存
  • 了解不同缓存管理器的特点

📦 缓存概述

Shiro提供了强大的缓存支持,可以显著提高系统性能,减少数据库访问次数。缓存主要应用于以下场景:

  • 认证缓存:缓存用户的认证信息,避免重复查询数据库
  • 授权缓存:缓存用户的角色和权限信息,避免重复查询数据库
  • 会话缓存:缓存用户会话信息,支持分布式会话管理

🔧 项目缓存实现

项目代码文件src/main/java/com/shiro/tutorial/CacheExample.java

packagecom.shiro.tutorial;importorg.apache.shiro.SecurityUtils;importorg.apache.shiro.authc.UsernamePasswordToken;importorg.apache.shiro.cache.MemoryConstrainedCacheManager;importorg.apache.shiro.mgt.DefaultSecurityManager;importorg.apache.shiro.realm.text.IniRealm;importorg.apache.shiro.subject.Subject;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;/** * Apache Shiro 缓存支持示例类 * 开发思路: * 1. 配置支持缓存的Realm和安全管理器 * 2. 实现用户身份认证 * 3. 演示Shiro缓存功能的使用 */publicclassCacheExample{// 创建日志记录器,用于输出程序运行信息privatestaticfinalLoggerlog=LoggerFactory.getLogger(CacheExample.class);/** * 程序入口方法 * 开发过程: * 1. 初始化Shiro安全框架组件,配置缓存管理器 * 2. 进行用户身份认证 * 3. 演示缓存功能 */publicstaticvoidmain(String[]args){// 创建 IniRealm 实例,使用专门的会话配置文件shiro-session.iniIniRealminiRealm=newIniRealm("classpath:shiro-session.ini");// 启用缓存 - 设置缓存管理器iniRealm.setCacheManager(newMemoryConstrainedCacheManager());// 启用认证缓存iniRealm.setAuthenticationCachingEnabled(true);// 启用授权缓存iniRealm.setAuthorizationCachingEnabled(true);// 创建 SecurityManager 安全管理器实例,并传入realmDefaultSecurityManagersecurityManager=newDefaultSecurityManager(iniRealm);// 将 SecurityManager 绑定到 SecurityUtils 工具类SecurityUtils.setSecurityManager(securityManager);// 获取当前执行用户(Subject)SubjectcurrentUser=SecurityUtils.getSubject();// 判断当前用户是否已经通过认证if(!currentUser.isAuthenticated()){// 如果未认证,则创建UsernamePasswordToken令牌UsernamePasswordTokentoken=newUsernamePasswordToken("user1","password1");try{// 执行用户登录认证currentUser.login(token);// 认证成功后,记录用户登录成功的日志信息log.info("用户 {} 登录成功!",currentUser.getPrincipal());}catch(Exceptione){log.error("认证失败",e);}}// 检查用户角色(第一次)booleanhasRoleFirst=currentUser.hasRole("admin");log.info("第一次检查用户是否有admin角色: {}",hasRoleFirst);// 再次检查用户角色(应该从缓存中获取)booleanhasRoleSecond=currentUser.hasRole("admin");log.info("第二次检查用户是否有admin角色: {}",hasRoleSecond);// 检查用户权限(第一次)booleanisPermittedFirst=currentUser.isPermitted("user:create");log.info("第一次检查用户是否有'user:create'权限: {}",isPermittedFirst);// 再次检查用户权限(应该从缓存中获取)booleanisPermittedSecond=currentUser.isPermitted("user:create");log.info("第二次检查用户是否有'user:create'权限: {}",isPermittedSecond);log.info("=== 缓存说明 ===");log.info("1. Shiro提供了多种缓存管理器实现:");log.info(" - MemoryConstrainedCacheManager: 基于内存的简单缓存管理器");log.info(" - EhCacheManager: 基于Ehcache的缓存管理器");log.info(" - SpringCacheManager: 基于Spring Cache的缓存管理器");log.info(" - RedisCacheManager: 基于Redis的缓存管理器");log.info("");log.info("2. 缓存配置要点:");log.info(" - 启用认证缓存: setAuthenticationCachingEnabled(true)");log.info(" - 启用授权缓存: setAuthorizationCachingEnabled(true)");log.info(" - 设置缓存名称: setAuthenticationCacheName() 和 setAuthorizationCacheName()");log.info("");log.info("3. 缓存的好处:");log.info(" - 减少数据库访问次数");log.info(" - 提高认证和授权检查的速度");log.info(" - 降低系统负载");// 正常退出程序System.exit(0);}}

� 代码执行逻辑

创建IniRealm实例
配置缓存管理器
启用认证缓存
启用授权缓存
创建DefaultSecurityManager实例
绑定到SecurityUtils
获取Subject
是否已认证?
跳过认证
创建UsernamePasswordToken
执行登录
登录成功?
记录登录成功日志
记录认证失败日志
第一次检查admin角色
记录第一次角色检查结果
第二次检查admin角色
记录第二次角色检查结果
第一次检查user:create权限
记录第一次权限检查结果
第二次检查user:create权限
记录第二次权限检查结果
输出缓存说明
退出程序

� 缓存工作原理

用户请求认证
认证缓存命中?
直接返回认证结果
查询数据库获取认证信息
存储到认证缓存
返回认证结果
用户请求授权
授权缓存命中?
直接返回授权结果
查询数据库获取授权信息
存储到授权缓存
返回授权结果
缓存管理器
MemoryConstrainedCacheManager
EhCacheManager
SpringCacheManager
RedisCacheManager

📚 缓存配置要点

  1. 选择合适的缓存管理器

    • MemoryConstrainedCacheManager:基于内存的简单缓存管理器,适合开发和测试环境
    • EhCacheManager:基于Ehcache的缓存管理器,适合单机环境
    • SpringCacheManager:与Spring Cache集成,适合Spring项目
    • RedisCacheManager:基于Redis的缓存管理器,适合分布式环境
  2. 配置缓存参数

    • 设置缓存名称
    • 设置缓存过期时间
    • 设置缓存大小限制
    • 配置缓存刷新策略
  3. 缓存清理

    • 手动清理缓存
    • 配置自动清理策略
    • 监听缓存事件

🏆 学习成果检验

✅ 实践任务
  1. 运行CacheExample类,观察缓存功能的效果
  2. 尝试使用不同的缓存管理器
  3. 修改缓存配置参数,观察不同配置的效果
  4. 实现缓存清理功能
  5. 学习如何监控缓存使用情况

第九章:JWT集成

🎯 学习目标

  • 理解JWT的概念和优势
  • 掌握Shiro集成JWT的方法
  • 学习JWT令牌的生成和验证
  • 了解无状态认证的实现
  • 掌握JWTRealm的开发
  • 学习JWT工具类的实现

📦 JWT概述

JWT(JSON Web Token)是一种用于在网络应用间传递声明的基于JSON的开放标准:

  • 无状态:服务器不需要存储会话信息,适合分布式系统
  • 自包含:令牌中包含了所有必要的用户信息
  • 跨平台:支持不同语言和平台
  • 安全:支持签名和加密
  • 可扩展:可以自定义声明内容

� JWT结构

JWT令牌由三部分组成,用点(.)分隔:

  1. Header:包含令牌类型和签名算法
  2. Payload:包含声明信息(如用户名、过期时间等)
  3. Signature:使用密钥对前两部分进行签名,用于验证令牌的完整性

🏗️ 项目JWT实现架构

JwtUtils
生成JWT令牌
验证JWT令牌
提取用户名
JwtToken
封装JWT令牌
实现AuthenticationToken接口
JwtRealm
doGetAuthenticationInfo
doGetAuthorizationInfo
validateToken
getUserRoles
getUserPermissions
JwtShiroExample
初始化JWT Realm
生成并验证JWT令牌
使用JWT令牌登录
验证角色和权限

�� JWT工具类实现

项目代码文件src/main/java/com/shiro/tutorial/jwt/JwtUtils.java

packagecom.shiro.tutorial.jwt;importio.jsonwebtoken.Jwts;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importjavax.crypto.SecretKey;importjava.util.Date;/** * JWT工具类 * 用于生成和验证JWT令牌 */publicclassJwtUtils{// 创建日志记录器,用于输出程序运行信息privatestaticfinalLoggerlog=LoggerFactory.getLogger(JwtUtils.class);// JWT 签名密钥publicstaticfinalSecretKeySECRET_KEY=Jwts.SIG.HS256.key().build();// 令牌过期时间(24小时)privatestaticfinallongEXPIRATION_TIME=86400000;/** * 生成 JWT 令牌 * @param username 用户名 * @return JWT 令牌字符串 */publicstaticStringgenerateToken(Stringusername){Datenow=newDate();DateexpirationDate=newDate(now.getTime()+EXPIRATION_TIME);returnJwts.builder().subject(username).issuedAt(now).expiration(expirationDate).signWith(SECRET_KEY).compact();}/** * 从 JWT 令牌中提取用户名 * @param token JWT 令牌 * @return 用户名 */publicstaticStringgetUsernameFromToken(Stringtoken){try{returnJwts.parser().verifyWith(SECRET_KEY).build().parseSignedClaims(token).getPayload().getSubject();}catch(Exceptione){log.error("解析 JWT 令牌失败",e);returnnull;}}/** * 验证 JWT 令牌的有效性 * @param token JWT 令牌 * @return 令牌是否有效 */publicstaticbooleanvalidateToken(Stringtoken){try{Jwts.parser().verifyWith(SECRET_KEY).build().parseSignedClaims(token);returntrue;}catch(Exceptione){log.error("JWT 令牌验证失败",e);returnfalse;}}}

💻 JWT令牌实现

项目代码文件src/main/java/com/shiro/tutorial/jwt/JwtToken.java

packagecom.shiro.tutorial.jwt;importorg.apache.shiro.authc.AuthenticationToken;/** * JWT令牌类 * 用于封装JWT令牌信息,实现Shiro的AuthenticationToken接口 */publicclassJwtTokenimplementsAuthenticationToken{/** * JWT 令牌字符串 */privatefinalStringtoken;/** * 构造函数 * @param token JWT 令牌字符串 */publicJwtToken(Stringtoken){this.token=token;}/** * 获取用户身份信息 * @return JWT 令牌字符串 */@OverridepublicObjectgetPrincipal(){returntoken;}/** * 获取用户凭据信息 * @return JWT 令牌字符串 */@OverridepublicObjectgetCredentials(){returntoken;}}

💻 JWT Realm实现

项目代码文件src/main/java/com/shiro/tutorial/jwt/JwtRealm.java

packagecom.shiro.tutorial.jwt;importio.jsonwebtoken.*;importorg.apache.shiro.authc.AuthenticationException;importorg.apache.shiro.authc.AuthenticationInfo;importorg.apache.shiro.authc.AuthenticationToken;importorg.apache.shiro.authc.SimpleAuthenticationInfo;importorg.apache.shiro.authz.AuthorizationInfo;importorg.apache.shiro.authz.SimpleAuthorizationInfo;importorg.apache.shiro.realm.AuthorizingRealm;importorg.apache.shiro.subject.PrincipalCollection;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importjava.util.HashSet;importjava.util.Set;/** * JWT Realm类 * 用于处理JWT令牌的认证和授权 */publicclassJwtRealmextendsAuthorizingRealm{// 创建日志记录器,用于输出程序运行信息privatestaticfinalLoggerlog=LoggerFactory.getLogger(JwtRealm.class);/** * 设置 Realm 支持的AuthenticationToken类型 * @param token 认证令牌 * @return 是否支持该令牌类型 */@Overridepublicbooleansupports(AuthenticationTokentoken){returntokeninstanceofJwtToken;}/** * 认证方法:获取认证信息 * @param authToken 认证令牌 * @return 认证信息 * @throws AuthenticationException 认证异常 */@OverrideprotectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationTokenauthToken)throwsAuthenticationException{log.info("JWT Realm 开始认证...");// 获取 JWT 令牌Stringtoken=(String)authToken.getCredentials();// 验证 JWT 令牌if(!validateToken(token)){thrownewAuthenticationException("无效的 JWT 令牌");}// 从 JWT 令牌中获取用户名Stringusername=getUsernameFromToken(token);// 返回认证信息returnnewSimpleAuthenticationInfo(username,token,getName());}/** * 授权方法:获取授权信息 * @param principals 身份信息 * @return 授权信息 */@OverrideprotectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollectionprincipals){log.info("JWT Realm 开始授权...");// 获取用户名Stringusername=(String)principals.getPrimaryPrincipal();// 创建授权信息对象SimpleAuthorizationInfoauthorizationInfo=newSimpleAuthorizationInfo();// 添加角色(模拟数据)Set<String>roles=getUserRoles(username);authorizationInfo.setRoles(roles);// 添加权限(模拟数据)Set<String>permissions=getUserPermissions(username);authorizationInfo.setStringPermissions(permissions);returnauthorizationInfo;}/** * 从 JWT 令牌中提取用户名 * @param token JWT 令牌 * @return 用户名 */privateStringgetUsernameFromToken(Stringtoken){try{returnJwts.parser().verifyWith(JwtUtils.SECRET_KEY).build().parseSignedClaims(token).getPayload().getSubject();}catch(JwtExceptione){log.error("解析 JWT 令牌失败",e);returnnull;}}/** * 验证 JWT 令牌的有效性 * @param token JWT 令牌 * @return 令牌是否有效 */privatebooleanvalidateToken(Stringtoken){try{Jwts.parser().verifyWith(JwtUtils.SECRET_KEY).build().parseSignedClaims(token);returntrue;}catch(JwtExceptione){log.error("JWT 令牌验证失败",e);returnfalse;}}/** * 获取用户角色(模拟实现) * @param username 用户名 * @return 用户角色集合 */privateSet<String>getUserRoles(Stringusername){Set<String>roles=newHashSet<>();// 模拟用户角色数据if("admin".equals(username)){roles.add("admin");roles.add("user");}elseif("user".equals(username)){roles.add("user");}returnroles;}/** * 获取用户权限(模拟实现) * @param username 用户名 * @return 用户权限集合 */privateSet<String>getUserPermissions(Stringusername){Set<String>permissions=newHashSet<>();// 模拟用户权限数据if("admin".equals(username)){permissions.add("user:*");permissions.add("product:*");}elseif("user".equals(username)){permissions.add("product:view");permissions.add("product:create");}returnpermissions;}}

💻 JWT集成示例

项目代码文件src/main/java/com/shiro/tutorial/jwt/JwtShiroExample.java

packagecom.shiro.tutorial.jwt;importorg.apache.shiro.SecurityUtils;importorg.apache.shiro.mgt.DefaultSecurityManager;importorg.apache.shiro.subject.Subject;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;/** * Apache Shiro JWT集成示例类 * 开发思路: * 1. 创建JWT工具类用于生成和验证JWT令牌 * 2. 创建JWT Realm处理JWT认证和授权 * 3. 配置安全管理器使用JWT Realm * 4. 演示JWT令牌的生成和验证过程 */publicclassJwtShiroExample{// 创建日志记录器,用于输出程序运行信息privatestaticfinalLoggerlog=LoggerFactory.getLogger(JwtShiroExample.class);/** * 程序入口方法 * 开发过程: * 1. 初始化Shiro安全框架组件,使用JWT Realm * 2. 生成JWT令牌 * 3. 使用JWT令牌进行用户身份认证和授权验证 */publicstaticvoidmain(String[]args){// 创建JWT Realm实例JwtRealmjwtRealm=newJwtRealm();// 创建 SecurityManager 安全管理器实例,并传入JWT RealmDefaultSecurityManagersecurityManager=newDefaultSecurityManager(jwtRealm);// 将 SecurityManager 绑定到 SecurityUtils 工具类SecurityUtils.setSecurityManager(securityManager);// 获取当前执行用户(Subject)SubjectcurrentUser=SecurityUtils.getSubject();// 生成JWT令牌(模拟用户登录成功后生成令牌)StringjwtToken=JwtUtils.generateToken("admin");log.info("生成的JWT令牌: {}",jwtToken);// 验证 JWT 令牌booleanisValid=JwtUtils.validateToken(jwtToken);log.info("JWT令牌是否有效: {}",isValid);// 从 JWT 令牌中提取用户名Stringusername=JwtUtils.getUsernameFromToken(jwtToken);log.info("从JWT令牌中提取的用户名: {}",username);// 使用 JWT 令牌进行认证JwtTokentoken=newJwtToken(jwtToken);try{// 使用 JWT 令牌进行用户登录认证currentUser.login(token);// 认证成功后,记录用户登录成功的日志信息log.info("用户 {} 使用JWT令牌登录成功!",currentUser.getPrincipal());}catch(Exceptione){log.error("JWT 令牌认证失败",e);}// 验证用户是否有 admin 角色if(currentUser.hasRole("admin")){log.info("用户具有 admin 角色");}else{log.info("用户不具有 admin 角色");}// 验证用户是否有user:delete权限if(currentUser.isPermitted("user:delete")){log.info("用户有删除用户的权限");}else{log.info("用户没有删除用户的权限");}System.exit(0);}}

🔄 代码执行逻辑

创建JwtRealm实例
创建DefaultSecurityManager实例
绑定到SecurityUtils
获取Subject
生成JWT令牌
输出JWT令牌
验证JWT令牌
令牌有效?
输出令牌有效日志
输出令牌无效日志
从令牌中提取用户名
输出提取的用户名
创建JwtToken对象
执行登录
登录成功?
记录登录成功日志
记录认证失败日志
检查admin角色
角色检查成功?
记录角色检查通过日志
记录角色检查失败日志
检查user:delete权限
权限检查成功?
记录权限检查通过日志
记录权限检查失败日志
退出程序

🔐 JWT安全最佳实践

  1. 使用强密钥:选择足够长且复杂的密钥,定期轮换
  2. 合理设置过期时间:根据业务需求设置适当的过期时间
  3. 使用HTTPS传输:确保JWT令牌在传输过程中不被窃取
  4. 不要存储敏感信息:令牌中只存储必要的非敏感信息
  5. 实现令牌刷新机制:支持令牌过期前的自动刷新
  6. 添加黑名单机制:支持主动吊销令牌
  7. 使用合适的签名算法:根据安全需求选择合适的签名算法

🏆 学习成果检验

✅ 实践任务
  1. 运行JwtShiroExample类,观察JWT集成的结果
  2. 学习JWT令牌的生成和验证过程
  3. 理解无状态认证的优势和实现方法
  4. 尝试修改JWT过期时间,观察不同过期时间的效果
  5. 尝试添加新的角色和权限到JwtRealm中
  6. 实现JWT令牌的刷新机制

🌟 扩展学习

  1. JWT与传统Session的对比:理解两种认证方式的优缺点
  2. JWT在分布式系统中的应用:学习如何在微服务架构中使用JWT
  3. JWT加密:了解如何对JWT令牌进行加密,保护敏感信息
  4. JWT扩展声明:学习如何自定义JWT声明,添加额外的用户信息
  5. JWT性能优化:了解如何优化JWT的生成和验证性能

🎓 总结与进阶

📋 核心知识点回顾

  1. 认证(Authentication):验证用户身份,确认用户是否为系统合法用户
  2. 授权(Authorization):检查用户权限,确定用户可以访问哪些资源
  3. 角色(Role):权限的集合,用于批量分配权限给用户
  4. 权限(Permission):对资源的访问许可,采用"资源:操作:实例"格式
  5. 会话(Session):管理用户的会话状态,支持跨环境使用
  6. 加密(Encryption):保护用户密码安全,支持多种加密算法
  7. Realm:连接Shiro与数据源的桥梁,负责获取安全数据
  8. JWT:无状态认证方式,适合分布式系统

🚀 进阶学习建议

  1. 深入学习Shiro源码:理解Shiro的内部实现机制
  2. 集成Spring Boot:学习如何在Spring Boot项目中使用Shiro
  3. 分布式会话管理:学习如何使用Redis等存储会话
  4. 多因素认证:实现更安全的认证方式
  5. 权限审计:实现权限使用情况的审计日志
  6. 性能优化:学习如何优化Shiro的性能

📚 推荐资源

  • 官方文档:Apache Shiro Documentation
  • GitHub仓库:apache/shiro
  • 中文教程:Apache Shiro中文文档
  • 示例项目:本教程配套的代码示例

🎉恭喜你完成了Apache Shiro完整教程的学习!

通过本教程的学习,你已经掌握了Apache Shiro的核心概念和使用方法,能够在实际项目中灵活应用Shiro实现安全认证和授权。

建议你继续深入学习Shiro的高级特性,并在实际项目中应用所学知识,不断提升自己的安全开发能力。


需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询