【Shiro】Shiro 的学习教程(一)之快速入门

2024-09-08 11:04

本文主要是介绍【Shiro】Shiro 的学习教程(一)之快速入门,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 1、Shiro 简介
  • 2、Shiro 认证、授权
    • 2.1、认证
    • 2.2、授权
  • 3、快速入门
  • 4、自定义 Realm
  • 5、加密
  • 6、实现授权

1、Shiro 简介

Shiro 官网:https://shiro.apache.org/

Shiro 是一个功能强大且易于使用的 Java 安全框架,它执行身份验证、授权、加密和会话管理。使用 Shiro 易于理解的 API,您可以快速轻松地保护任何应用程序—从最小的移动应用程序到最大的 web 和企业应用程序

Shiro 的架构图如下:

在这里插入图片描述
各组件说明如下:

  • Subject:即主体,外部应用与 Subject 进行交互,Subject 记录了当前的操作用户,将用户的概念理解为当前操作的主体。外部程序通过 Subject 进行认证授权,而 Subject 是通过SecurityManager 安全管理器进行认证授权
  • SecurityManager:即安全管理器,对全部的 Subject 进行安全管理,它是 Shiro 的核心,负责对所有的 Subject 进行安全管理。通过 SecurityManager 可以完成 Subject的认证、授权等,实质上SecurityManager 是通过 Authenticator 进行认证,通过 Authorizer 进行授权,通过 SessionManager 进行会话管理等(SecurityManager 是一个接口,继承了 AuthenticatorAuthorizerSessionManager 这三个接口)
  • Authenticator:即认证器,对用户身份进行认证,Authenticator 是一个接口,Shiro 提供 ModularRealmAuthenticator 实现类,通过 ModularRealmAuthenticator 基本上可以满足大多数需求;也可以自定义认证器
  • Authorizer:即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限
  • Realm:即领域,相当于 Datasource 数据源,SecurityManager 进行安全认证,需要通过 Realm 获取用户权限数据,比如:如果用户身份数据在数据库,那么 Realm 就需要从数据库获取用户身份信息。LDAP 数据源的 JndiLdapRealm,JDBC数 据源的 JdbcRealm,ini 文件数据源的 iniRealm,Properties 文件数据源的 PropertiesRealm,等等,我们也可以插入自己的 Realm 实现来代表自定义的数据源
  • SessionManager:即会话管理,Shiro 框架定义了一套会话管理,它不依赖 web 容器的 session,所以 Shiro 可以使用在非 web 应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录
  • SessionDAO:即会话 dao,是对 session 会话操作的一套接口,比如要将 session 存储到数据库,可以通过 jdbc 将会话存储到数据库
  • CacheManager:即缓存管理,将用户权限数据存储在缓存,这样可以提高性能
  • Cryptography:即密码管理,Shiro 提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能

2、Shiro 认证、授权

2.1、认证

身份认证:就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确

在认证过程中,涉及三个概念:

  • Subject:主体。主体可以是用户、程序等,进行认证的都称为主体
  • Principal:身份信息,是主体(Subject)进行身份认证的标识,标识必须具有唯一性。如:用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)
  • Credential:凭证信息。只有主体自己知道的安全信息,如密码、证书等

2.2、授权

授权方式:

  1. 基于角色
  2. 基于资源

权限字符串

权限字符串的规则是:资源标识符:操作:资源实例标识符,意思是:对哪个资源的哪个实例具有什么操作,“:”是资源/操作/实例的分割符,权限字符串也可以使用 * 通配符

如:

  • 用户创建权限:user:create,或user:create:*
  • 用户修改实例 001 的权限:user:update:001
  • 用户实例 001 的所有权限:user:*:001

3、快速入门

引入 POM 依赖:

<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.5.3</version>
</dependency>

shiro.ini:位于 resources 资源目录下

# 约定写法
[users]
# 用户名=密码(,角色)
christy=123456,admin
zzc=666666,super
tide=654321,guest[roles]
super=*
admin=guest:*
guest=user:delete:zhangsan

测试类:

public class ShiroAuthenticatorTest2 {public static void main(String[] args) {//准备阶段//// 1.创建安全管理器对象DefaultSecurityManager securityManager = new DefaultSecurityManager();// 2.给安全管理器设置 RealmsecurityManager.setRealm(new IniRealm("classpath:shiro.ini"));// 3.给全局安全工具类 SecurityUtils 设置安全管理器SecurityUtils.setSecurityManager(securityManager);// 4.拿到当前的 SubjectSubject subject = SecurityUtils.getSubject();// 5.创建令牌AuthenticationToken token = new UsernamePasswordToken("christy","123456");try {//认证阶段//// 6.用户认证System.out.println("认证状态:" + subject.isAuthenticated());subject.login(token);System.out.println("认证状态:" + subject.isAuthenticated());} catch (UnknownAccountException e){e.printStackTrace();System.out.println("认证失败:用户不存在!");} catch (IncorrectCredentialsException e){e.printStackTrace();System.out.println("认证失败:密码不正确!");} catch (Exception e){e.printStackTrace();}//授权//// 7.角色授权System.out.println(subject.hasRole("admin"));// 8.资源授权System.out.println(subject.isPermitted("guest:update:01"));}
}

4、自定义 Realm

查看源代码知:有两个抽象方法:

  • 认证AuthenticatingRealm#doGetAuthenticationInfo(AuthenticationToken)
  • 授权AuthorizingRealm#doGetAuthorizationInfo(PrincipalCollection)

且:AuthorizingRealm 继承于 AuthenticatingRealm。意味着自定义的 Reaml 只要继承 AuthorizingRealm,便可重写认证、授权方法

自定义 Reaml:

public class CustomerRealm extends AuthorizingRealm {// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}// 认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 在token中获取用户名String principal = (String) authenticationToken.getPrincipal();System.out.println(principal);// 模拟根据身份信息从数据库查询if("christy".equals(principal)){// 参数说明:用户名 | 密码 | 当前realm的名字return new SimpleAuthenticationInfo(principal,"123456", this.getName());}return null;}}

测试:

DefaultSecurityManager securityManager = new DefaultSecurityManager();
securityManager.setRealm(new CustomerRealm());

5、加密

Shiro 中提供了一整套的加密算法,并且提供了随机盐。Shiro 使用指定的加密算法将用户密码和随机盐进行加密,并按照指定的散列次数将散列后的密码存储在数据库中。由于随机盐每个用户可以不同,这就极大的提高了密码的安全性

Md5Hash:有三个构造方法:

  1. public Md5Hash(Object source):只加密
  2. public Md5Hash(Object source, Object salt):加密、加盐
  3. public Md5Hash(Object source, Object salt, int hashIterations):加密、加盐、加散列次数

加密后的 Realm:

public class CustomerRealm extends AuthorizingRealm {@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 在token中获取用户名String principal = (String) authenticationToken.getPrincipal();System.out.println(principal);// 模拟根据身份信息从数据库查询if("christy".equals(principal)){// 参数说明:用户名 | 密码(加密后的密码) | 盐 | 当前realm的名字return new SimpleAuthenticationInfo(principal,"41a4e25bcf1272844e38b19047dd68a0", ByteSource.Util.bytes("1q2w3e"), this.getName());}return null;}}

加密后的密码如下:

Md5Hash md5Hash03 = new Md5Hash("123456","1q2w3e", 1024);
// 41a4e25bcf1272844e38b19047dd68a0
System.out.println(md5Hash03.toHex());

测试:

public class ShiroAuthenticatorTest {public static void main(String[] args) {// 1.创建安全管理器对象DefaultSecurityManager securityManager = new DefaultSecurityManager();// 2.给安全管理器设置 Realm//securityManager.setRealm(new IniRealm("classpath:shiro.ini"));CustomerRealm customerRealm = new CustomerRealm();// 3.为 Realm 设置凭证匹配器HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();// 设置加密算法credentialsMatcher.setHashAlgorithmName("md5");// 设置hash次数credentialsMatcher.setHashIterations(1024);customerRealm.setCredentialsMatcher(credentialsMatcher);securityManager.setRealm(customerRealm);// 4.给全局安全工具类 SecurityUtils 设置安全管理器SecurityUtils.setSecurityManager(securityManager);// ...  }
}

【注意】:此代码中给 Reaml 设置了 CredentialsMatcher(HashedCredentialsMatcher),代码默认使用 SimpleCredentialsMatcher

CredentialsMatcher 结构图如下:

在这里插入图片描述

6、实现授权

CustomerRealm2

public class CustomerRealm2 extends AuthorizingRealm {// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {// 从系统返回的身份信息集合中获取主身份信息(用户名)String primaryPrincipal = (String)principalCollection.getPrimaryPrincipal();System.out.println("用户名: "+primaryPrincipal);// TODO 根据用户名获取当前用户的角色信息,以及权限信息SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();//将数据库中查询角色信息赋值给权限对象simpleAuthorizationInfo.addRole("admin");simpleAuthorizationInfo.addRole("user");//将数据库中查询权限信息赋值个权限对象simpleAuthorizationInfo.addStringPermission("user:*:01");simpleAuthorizationInfo.addStringPermission("product:create");return simpleAuthorizationInfo;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 在token中获取用户名String principal = (String) authenticationToken.getPrincipal();System.out.println(principal);// 模拟根据身份信息从数据库查询if("christy".equals(principal)){// 参数说明:用户名 | 密码 | 当前realm的名字return new SimpleAuthenticationInfo(principal,"123456", this.getName());}return null;}}

ShiroAuthenticatorTest3

public class ShiroAuthenticatorTest3 {public static void main(String[] args) {// 1.创建安全管理器对象DefaultSecurityManager securityManager = new DefaultSecurityManager();// 2.给安全管理器设置 RealmCustomerRealm2 customerRealm = new CustomerRealm2();securityManager.setRealm(customerRealm);// 3.给全局安全工具类 SecurityUtils 设置安全管理器SecurityUtils.setSecurityManager(securityManager);// 4.拿到当前的 SubjectSubject subject = SecurityUtils.getSubject();// 5.创建令牌AuthenticationToken token = new UsernamePasswordToken("christy","123456");try {// 6、用户认证System.out.println("认证状态:" + subject.isAuthenticated());subject.login(token);System.out.println("认证状态:" + subject.isAuthenticated());} catch (UnknownAccountException e){e.printStackTrace();System.out.println("认证失败:用户不存在!");} catch (IncorrectCredentialsException e){e.printStackTrace();System.out.println("认证失败:密码不正确!");} catch (Exception e){e.printStackTrace();}if (subject.isAuthenticated()) {// 基于角色权限控制System.out.println(subject.hasRole("admin"));//基于多角色权限控制(同时具有)System.out.println(subject.hasAllRoles(Arrays.asList("admin", "super")));//是否具有其中一个角色boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "super", "user"));for (boolean aBoolean : booleans) {System.out.println(aBoolean);}System.out.println("==============================================");System.out.println("权限:"+subject.isPermitted("user:update:01"));System.out.println("权限:"+subject.isPermitted("product:create:02"));//分别具有那些权限boolean[] permitted = subject.isPermitted("user:*:01", "order:*:10");for (boolean b : permitted) {System.out.println(b);}//同时具有哪些权限boolean permittedAll = subject.isPermittedAll("user:*:01", "product:create:01");System.out.println(permittedAll);}}
}

这篇关于【Shiro】Shiro 的学习教程(一)之快速入门的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

从入门到精通详解Python虚拟环境完全指南

《从入门到精通详解Python虚拟环境完全指南》Python虚拟环境是一个独立的Python运行环境,它允许你为不同的项目创建隔离的Python环境,下面小编就来和大家详细介绍一下吧... 目录什么是python虚拟环境一、使用venv创建和管理虚拟环境1.1 创建虚拟环境1.2 激活虚拟环境1.3 验证虚

基于C#实现PDF转图片的详细教程

《基于C#实现PDF转图片的详细教程》在数字化办公场景中,PDF文件的可视化处理需求日益增长,本文将围绕Spire.PDFfor.NET这一工具,详解如何通过C#将PDF转换为JPG、PNG等主流图片... 目录引言一、组件部署二、快速入门:PDF 转图片的核心 C# 代码三、分辨率设置 - 清晰度的决定因

Java Scanner类解析与实战教程

《JavaScanner类解析与实战教程》JavaScanner类(java.util包)是文本输入解析工具,支持基本类型和字符串读取,基于Readable接口与正则分隔符实现,适用于控制台、文件输... 目录一、核心设计与工作原理1.底层依赖2.解析机制A.核心逻辑基于分隔符(delimiter)和模式匹

Python多线程实现大文件快速下载的代码实现

《Python多线程实现大文件快速下载的代码实现》在互联网时代,文件下载是日常操作之一,尤其是大文件,然而,网络条件不稳定或带宽有限时,下载速度会变得很慢,本文将介绍如何使用Python实现多线程下载... 目录引言一、多线程下载原理二、python实现多线程下载代码说明:三、实战案例四、注意事项五、总结引

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

spring AMQP代码生成rabbitmq的exchange and queue教程

《springAMQP代码生成rabbitmq的exchangeandqueue教程》使用SpringAMQP代码直接创建RabbitMQexchange和queue,并确保绑定关系自动成立,简... 目录spring AMQP代码生成rabbitmq的exchange and 编程queue执行结果总结s

C#使用Spire.XLS快速生成多表格Excel文件

《C#使用Spire.XLS快速生成多表格Excel文件》在日常开发中,我们经常需要将业务数据导出为结构清晰的Excel文件,本文将手把手教你使用Spire.XLS这个强大的.NET组件,只需几行C#... 目录一、Spire.XLS核心优势清单1.1 性能碾压:从3秒到0.5秒的质变1.2 批量操作的优雅

Java List 使用举例(从入门到精通)

《JavaList使用举例(从入门到精通)》本文系统讲解JavaList,涵盖基础概念、核心特性、常用实现(如ArrayList、LinkedList)及性能对比,介绍创建、操作、遍历方法,结合实... 目录一、List 基础概念1.1 什么是 List?1.2 List 的核心特性1.3 List 家族成

Python学习笔记之getattr和hasattr用法示例详解

《Python学习笔记之getattr和hasattr用法示例详解》在Python中,hasattr()、getattr()和setattr()是一组内置函数,用于对对象的属性进行操作和查询,这篇文章... 目录1.getattr用法详解1.1 基本作用1.2 示例1.3 原理2.hasattr用法详解2.