shiro 学习笔记
1. 权限管理
1.1 什么是权限管理?
权限管理实现对用户访问系统的控制,按照安全规则或者安全策略,可以控制用户只能访问自己被授权的资源
权限管理包括用户身份认证和授权两部分,简称认证授权
1.2 什么是身份认证?
身份认证就是判断一个用户是否为合法用户的处理过程,最常用的方式就是通过核对用户输入和用户名和口令是否与系统中存储的一致,来判断用户身份是否正确
1.3 什么是授权?
授权,即访问控制,控制谁能访问哪些资源,主体进行身份认证后需要分配权限方可访问系统资源,对于某些资源没有权限是无法访问的
2. 什么是 shiro?
shiro 是一个功能强大且易于使用的 Java 安全框架,能够完成身份认证、授权、加密和会话管理等功能
2.1 shiro 的核心架构

Subject
主体,外部应用与 subject 进行交互,subject 记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject 在 shiro 中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过 subject 进行认证授,而 subject 是通过 SecurityManager 安全管理器进行认证授权
SecurityManager
安全管理器,对全部的 subject 进行安全管理,是 shiro 的核心。通过 SecurityManager 可以完成 subject 的认证、授权等,实质上 SecurityManager 是通过 Authenticator 进行认证,通过 Authorizer 进行授权,通过 SessionManager 进行会话管理。SecurityManager 是一个接口,继承了 Authenticator,Authorizer,SessionManager 这三个接口
Authenticator
认证器,对用户身份进行认证,Authenticator 是一个接口,shiro 提供 ModularRealmAuthenticator 实现类,通过 ModularRealmAuthenticator 基本上可以满足大多数需求,也可以自定义认证器
Authorizer
授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限
Realm
领域,相当于 datasource 数据源,securityManager 进行安全认证需要通过 Realm 获取用户权限数据,比如:如果用户身份数据在数据库,那么 realm 就需要从数据库获取用户身份信息
SessionManager
会话管理,shiro 框架定义了一套会话管理,它不依赖 web 容器的 session,所以 shiro 可以使用在非 web 应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录
SessionDAO
会话 dao,是对 session 会话操作的一套接口,比如要将 session 存储到数据库,可以通过 jdbc 将会话存储到数据库
CacheManager
CacheManager 即缓存管理,将用户权限数据存储在缓存,这样可以提高性能
Cryptography
密码管理,shiro 提供了一套加解密的组件,方便开发,比如提供常用的散列、加解密等功能
3. shiro 中的认证
3.1 认证
身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确
3.2 shiro 认证中的关键对象
Subject:主体
访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体
Principal:身份信息
是主体(subject)进行身份认证的标识,标识必须具有唯一性, 如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)
credential:凭证信息
是只有主体自己知道的安全信息,如密码、证书等
3.3 认证流程

3.4 认证开发
3.4.1 引入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.3</version>
</dependency>
3.4.2 创建配置文件
该配置文件用来书写系统中相关的权限数据,主要用于学习使用
[users]
xiaochen=123
zhangsan=456
3.4.3 开发认证代码
public class TestAuthenticator {
public static void main(String[] args) {
// 1.创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 2.给安全管理器设置 realm
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
// 3.给 SecurityUtils 全局安全工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
// 4.获取认证主体
Subject subject = SecurityUtils.getSubject();
// 5.创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");
// 6.认证
try {
System.out.println("认证状态:" + subject.isAuthenticated());
subject.login(token);
System.out.println("认证状态:" + subject.isAuthenticated());
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.5 自定义 Realm
自定义 Realm 的实现,即是将认证/授权数据来源转为数据库的实现
public class TestCustomerRealmAuthenticator {
public static void main(String[] args) {
// 1.创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 2.设置自定义realm
securityManager.setRealm(new CustomerRealm());
// 3.给 SecurityUtils 全局安全工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
// 4.获取认证主体
Subject subject = SecurityUtils.getSubject();
// 5.创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");
// 6.认证
try {
System.out.println("认证状态:" + subject.isAuthenticated());
subject.login(token);
System.out.println("认证状态:" + subject.isAuthenticated());
} catch (Exception e) {
e.printStackTrace();
}
}
}
自定义 Realm 代码实现
public class CustomerRealm extends AuthorizingRealm {
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 1. 在token中获取用户名
String principal = (String) authenticationToken.getPrincipal();
// 2. 查询数据库,此处模拟数据库数据
if ("xiaochen".equals(principal)) {
// 参数1:正确的用户名
// 参数2:正确的密码
// 参数3:提供当前realm的名字
SimpleAuthenticationInfo simpleAuthenticationInfo
= new SimpleAuthenticationInfo("xianchen", "123", this.getName());
return simpleAuthenticationInfo;
}
return null;
}
}
3.6 明文加密
实际使用时,我们不可能把用户密码以明文形式显示,需要做加密处理
通常的加密方式是使用 md5 + salt + hash 散列的形式,校验过程:保存盐和散列后的值,在 shiro 完成密码校验
下面是使用 shiro 提供的 api 完成加密代码示例
public class TestShiroMD5 {
public static void main(String[] args) {
// 1. 使用md5加密
// 参数1是明文密码
Md5Hash md5Hash1 = new Md5Hash("123");
// 打印加密后的密文
// 结果:202cb962ac59075b964b07152d234b70
System.out.println(md5Hash1.toHex());
// 2. 使用md5+salt加密
Md5Hash md5Hash2 = new Md5Hash("123", "X0*7ps");
// 8a83592a02263bfe6752b2b5b03a4799
System.out.println(md5Hash2.toHex());
// 3. 使用md5+salt+hash散列加密
Md5Hash md5Hash3 = new Md5Hash("123", "X0*7ps", 1024);
// e4f9bf3e0c58f045e62c23c533fcf633
System.out.println(md5Hash3.toHex());
}
}
自定义 CustomerMd5Realm
public class CustomerMd5Realm extends AuthorizingRealm {
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 1. 在token中获取用户名
String principal = (String) authenticationToken.getPrincipal();
// 2. 查询数据库,此处模拟数据库数据
if ("xiaochen".equals(principal)) {
// 参数1:正确的用户名
// 参数2:正确的密码
// 参数3:提供当前realm的名字
// md5
// return new SimpleAuthenticationInfo(principal, "202cb962ac59075b964b07152d234b70", this.getName());
// md5+salt
/*return new SimpleAuthenticationInfo(principal, "8a83592a02263bfe6752b2b5b03a4799", ByteSource.Util.bytes("X0*7ps"), this.getName());*/
}
return null;
}
}
校验流程
public class TestCustomerMd5RealmAuthenticator {
public static void main(String[] args) {
// 1.创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 2.设置自定义realm
CustomerMd5Realm realm = new CustomerMd5Realm();
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 使用MD5加密
hashedCredentialsMatcher.setHashAlgorithmName("md5");
// 散列次数
hashedCredentialsMatcher.setHashIterations(1024);
realm.setCredentialsMatcher(hashedCredentialsMatcher);
securityManager.setRealm(realm);
// 3.给 SecurityUtils 全局安全工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
// 4.获取认证主体
Subject subject = SecurityUtils.getSubject();
// 5.创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");
// 6.认证
try {
System.out.println("认证状态:" + subject.isAuthenticated());
subject.login(token);
System.out.println("认证状态:" + subject.isAuthenticated());
} catch (Exception e) {
e.printStackTrace();
}
}
}
4. shiro 中的授权
4.1 授权
授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的
4.2 关键对象
授权可简单理解为 who 对 what(which) 进行 How 操作:
- Who,即主体(Subject),主体需要访问系统中的资源
- What,即资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型和资源实例,比如商品信息为资源类型,类型为 t01 的商品为资源实例,编号为 001 的商品信息也属于资源实例
- How,权限/许可(Permission),规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为 001 用户的修改权限等,通过权限可知道主体对哪些资源都有哪些操作许可

4.3 授权方式
基于角色的访问控制,以角色为中心进行访问控制
if(subject.hasRole("admin")){
//操作什么资源
}
基于资源的访问控制,以资源为中心进行访问控制
if(subject.isPermission("user:update:01")){ //资源实例
//对01用户进行修改
}
if(subject.isPermission("user:update:*")){ //资源类型
//对01用户进行修改
}
4.4 权限字符串
权限字符串的规则是:资源标识符:操作:资源实例标识符,意思是对哪个资源的哪个实例具有什么操作,: 是资源/操作/实例的分割符,权限字符串也可以使用 * 通配符
例子:
- 用户创建权限:
user:create,或user:create:* - 用户修改实例 001 的权限:
user:update:001 - 用户实例 001 的所有权限:
user:*:001
4.5 授权编程实现
在之前 md5 加密的基础上,实现授权操作
自定义 CustomerMd5Realm
public class CustomerMd5Realm extends AuthorizingRealm {
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获取身份信息
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
// 根据身份信息(用户名)从数据库获取当前用户的角色以及权限信息
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
// 将数据库中查询的角色信息赋值给权限对象
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addRole("user");
// 将数据库中查询的权限信息赋值给权限对象
simpleAuthorizationInfo.addStringPermission("user:*:01");
simpleAuthorizationInfo.addStringPermission("product:create:02");
return simpleAuthorizationInfo;
}
...
}
授权逻辑
public class TestCustomerMd5RealmAuthenticator {
public static void main(String[] args) {
...
// 7. 认证用户进行授权
if (subject.isAuthenticated()) {
// 7.1 基于角色权限控制
boolean hasRole = subject.hasRole("admin");
System.out.println("角色校验:" + hasRole);
// 7.2 基于多角色权限控制
boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("admin", "user"));
System.out.println("多角色校验:" + hasAllRoles);
// 7.3 是否具有其中一个角色
boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user", "super"));
for (boolean aBoolean : booleans) {
System.out.println(aBoolean);
}
// 7.4 基于权限字符串的访问控制
boolean permitted = subject.isPermitted("user:*:01");
System.out.println("资源权限校验:" + permitted);
// 7.5 分布具有哪些资源权限
boolean[] permitted1 = subject.isPermitted("user:*:01", "order:*:10");
for (boolean b : permitted1) {
System.out.println(b);
}
// 7.6 同时具有哪些资源权限
boolean permittedAll = subject.isPermittedAll("user:*:01", "product:*");
System.out.println("多资源权限校验:" + permittedAll);
}
}
}
shiro 学习笔记的更多相关文章
- Shiro学习笔记(5)——web集成
Web集成 shiro配置文件shiroini 界面 webxml最关键 Servlet 測试 基于 Basic 的拦截器身份验证 Web集成 大多数情况.web项目都会集成spring.shiro在 ...
- shiro学习笔记_0600_自定义realm实现授权
博客shiro学习笔记_0400_自定义Realm实现身份认证 介绍了认证,这里介绍授权. 1,仅仅通过配置文件来指定权限不够灵活且不方便.在实际的应用中大多数情况下都是将用户信息,角色信息,权限信息 ...
- Shiro学习笔记总结,附加" 身份认证 "源码案例(一)
Shiro学习笔记总结 内容介绍: 一.Shiro介绍 二.subject认证主体 三.身份认证流程 四.Realm & JDBC reaml介绍 五.Shiro.ini配置介绍 六.源码案例 ...
- Shiro 学习笔记(一)——shiro简介
Apache Shiro 是一个安全框架.说白了,就是进行一下 权限校验,判断下这个用户是否登录了,是否有权限去做这件事情. Shiro 可以帮助我们完成:认证.授权.加密.会话管理.与web 集成. ...
- shiro学习笔记_0100_shiro简介
前言:第一次知道shiro是2016年夏天,做项目时候我要写springmvc的拦截器,申哥看到后,说这个不安全,就给我捣鼓了shiro,我就看了下,从此认识了shiro.此笔记是根据网上的视频教程记 ...
- Shiro学习笔记四(Shiro集成WEB)
这两天由于家里出了点事情,没有准时的进行学习.今天补上之前的笔记 -----没有学不会的技术,只有不停找借口的人 学习到的知识点: 1.Shiro 集成WEB 2.基于角色的权限控制 3.基于权限的控 ...
- Shiro学习笔记 三(认证授权)
第一种首先基于角色的权限控制 1.由于不断的创建SecurityFactory工程等步骤重复多次,所以应该将这些步骤封装成一个工具类 还是首先看一下目录结构 主要用到文件 首先贴一下工具类的方法 pa ...
- shiro学习笔记_0400_自定义realm实现身份认证
自定义Realm实现身份认证 先来看下Realm的类继承关系: Realm接口有三个方法,最重要的是第三个方法: a) String getName():返回此realm的名字 b) boolean ...
- Shiro 学习笔记(二)——shiro身份验证
身份验证: 在应用中证明他就是他本人.一般上用身份证.用户/密码 来证明. 在shiro中,用户需要提供principals (身份)和credentials(证明)给shiro,从而应用能验证用户身 ...
随机推荐
- Mac录屏同时录制系统声音和画外音(Soundflower无法安装解决方案)
个人博客地址:xzajyjs.cn 前言 以前一直有录屏的需求,但苦于自带的QuickTime 无法录制内屏声音,一直使用的是第三方的app.近期开腾讯会议需要录屏,但主持人本身没有开启录屏权限,只好 ...
- css如何简单设置文字溢出盒子显示省略号
1.单行文本溢出显示省略号单行文本溢出显示省略号,必须满足三个条件:(1)先强制一行内显示文本white-space:nowrap;(默认 normal自动换行)(2)超出的部分隐藏overflow: ...
- python解释器和Pycharm编辑器安装使用完整详细教程
一.官网下载或软件管家公众号下载 二.安装Python解释器 1.选择自定义安装并添加到环境变量 2.检验Python是否安装成功 三.安装pycharm编辑器 1.点击安装,修改安装路径,建议安装C ...
- 【UE4 C++ 基础知识】<7> 容器——TSet
概述 TSet是一种快速容器类,(通常)用于在排序不重要的情况下存储唯一元素. TSet 类似于 TMap 和 TMultiMap,但有一个重要区别:TSet 是通过对元素求值的可覆盖函数,使用数据值 ...
- .Net 5下的单文件部署
由于.net程序没有静态链接,一直缺乏单文件部署这种干净的发布方案.对客户端程序发布并不是很友好.在之前的.net framework下,有ILMerge合并程序集,以及LibZ的嵌入资源文件等第三方 ...
- 记一次 .NET 某资讯论坛 CPU爆高分析
大概有11天没发文了,真的不是因为懒,本想前几天抽空写,不知道为啥最近求助的朋友比较多,一天都能拿到2-3个求助dump,晚上回来就是一顿分析,有点意思的是大多朋友自己都分析了几遍或者公司多年的牛皮藓 ...
- 嵌入式单片机之stm32串口你懂了多少!!
stm32作为现在嵌入式物联网单片机行业中经常要用多的技术,相信大家都有所接触,今天这篇就给大家详细的分析下有关于stm32的出口,还不是很清楚的朋友要注意看看了哦,在最后还会为大家分享有些关于stm ...
- Jquery校验中国身份证号码是否正确
在项目中使用表单时经常会涉及到身份证号码是否正确的校验,下面看看应该中国二代身份证号码应该怎么用Jquery校验呢? 二代身份证校验码的计算方法 二代身份证由17位数字和一位校验码组成,那么校验方法是 ...
- Luogu P2822 [NOIp2016提高组]组合数问题 | 数学、二维前缀和
题目链接 思路:组合数就是杨辉三角,那么我们只要构造一个杨辉三角就行了.记得要取模,不然会爆.然后,再用二维前缀和统计各种情况下组合数是k的倍数的方案数.询问时直接O(1)输出即可. #include ...
- Swift进阶-内存管理
本文的主要目的是探索 RefCount 的内存结构及强/弱引用计数管理 Swift 中也是采用 ARC 编译器自动内存管理机制. Swift 对象的内存结构是 HeapObject, 有两个属性 Me ...