【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

相关文章

通过Docker Compose部署MySQL的详细教程

《通过DockerCompose部署MySQL的详细教程》DockerCompose作为Docker官方的容器编排工具,为MySQL数据库部署带来了显著优势,下面小编就来为大家详细介绍一... 目录一、docker Compose 部署 mysql 的优势二、环境准备与基础配置2.1 项目目录结构2.2 基

Linux安装MySQL的教程

《Linux安装MySQL的教程》:本文主要介绍Linux安装MySQL的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux安装mysql1.Mysql官网2.我的存放路径3.解压mysql文件到当前目录4.重命名一下5.创建mysql用户组和用户并修

最新Spring Security实战教程之Spring Security安全框架指南

《最新SpringSecurity实战教程之SpringSecurity安全框架指南》SpringSecurity是Spring生态系统中的核心组件,提供认证、授权和防护机制,以保护应用免受各种安... 目录前言什么是Spring Security?同类框架对比Spring Security典型应用场景传统

最新Spring Security实战教程之表单登录定制到处理逻辑的深度改造(最新推荐)

《最新SpringSecurity实战教程之表单登录定制到处理逻辑的深度改造(最新推荐)》本章节介绍了如何通过SpringSecurity实现从配置自定义登录页面、表单登录处理逻辑的配置,并简单模拟... 目录前言改造准备开始登录页改造自定义用户名密码登陆成功失败跳转问题自定义登出前后端分离适配方案结语前言

Centos环境下Tomcat虚拟主机配置详细教程

《Centos环境下Tomcat虚拟主机配置详细教程》这篇文章主要讲的是在CentOS系统上,如何一步步配置Tomcat的虚拟主机,内容很简单,从目录准备到配置文件修改,再到重启和测试,手把手带你搞定... 目录1. 准备虚拟主机的目录和内容创建目录添加测试文件2. 修改 Tomcat 的 server.X

Python中的输入输出与注释教程

《Python中的输入输出与注释教程》:本文主要介绍Python中的输入输出与注释教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、print 输出功能1. 基础用法2. 多参数输出3. 格式化输出4. 换行控制二、input 输入功能1. 基础用法2. 类

Java导入、导出excel用法步骤保姆级教程(附封装好的工具类)

《Java导入、导出excel用法步骤保姆级教程(附封装好的工具类)》:本文主要介绍Java导入、导出excel的相关资料,讲解了使用Java和ApachePOI库将数据导出为Excel文件,包括... 目录前言一、引入Apache POI依赖二、用法&步骤2.1 创建Excel的元素2.3 样式和字体2.

C++快速排序超详细讲解

《C++快速排序超详细讲解》快速排序是一种高效的排序算法,通过分治法将数组划分为两部分,递归排序,直到整个数组有序,通过代码解析和示例,详细解释了快速排序的工作原理和实现过程,需要的朋友可以参考下... 目录一、快速排序原理二、快速排序标准代码三、代码解析四、使用while循环的快速排序1.代码代码1.由快

Spring Boot拦截器Interceptor与过滤器Filter详细教程(示例详解)

《SpringBoot拦截器Interceptor与过滤器Filter详细教程(示例详解)》本文详细介绍了SpringBoot中的拦截器(Interceptor)和过滤器(Filter),包括它们的... 目录Spring Boot拦截器(Interceptor)与过滤器(Filter)详细教程1. 概述1

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx