【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

相关文章

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

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

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

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

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

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

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析

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

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

Python 安装和配置flask, flask_cors的图文教程

《Python安装和配置flask,flask_cors的图文教程》:本文主要介绍Python安装和配置flask,flask_cors的图文教程,本文通过图文并茂的形式给大家介绍的非常详细,... 目录一.python安装:二,配置环境变量,三:检查Python安装和环境变量,四:安装flask和flas

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

Ubuntu中远程连接Mysql数据库的详细图文教程

《Ubuntu中远程连接Mysql数据库的详细图文教程》Ubuntu是一个以桌面应用为主的Linux发行版操作系统,这篇文章主要为大家详细介绍了Ubuntu中远程连接Mysql数据库的详细图文教程,有... 目录1、版本2、检查有没有mysql2.1 查询是否安装了Mysql包2.2 查看Mysql版本2.

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J