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,从而应用能验证用户身 ...
随机推荐
- 2020.5.16-ICPC Central Europe Regional Contest 2019
A. ABB #include <bits/stdc++.h> using namespace std; #define PB push_back #define ZERO (1e-10) ...
- 高效动画实现原理-Jetpack Compose 初探索
一.简介 Jetpack Compose是Google推出的用于构建原生界面的新Android 工具包,它可简化并加快 Android上的界面开发.Jetpack Compose是一个声明式的UI框架 ...
- HashMap、ConcurrentHashMap红黑树实现分析
本文学习知识点 1.二叉查找树,以及二叉树查找带来的问题. 2.平衡二叉树及好处. 3.红黑树的定义及构造. 4.ConcurrentHashMap中红黑树的构造. 在正式分析红黑树之前,有必要了解红 ...
- Java:并发笔记-05
Java:并发笔记-05 说明:这是看了 bilibili 上 黑马程序员 的课程 java并发编程 后做的笔记 4. 共享模型之内存 本章内容 上一章讲解的 Monitor 主要关注的是访问共享变量 ...
- Java:修饰符小记
Java:修饰符小记 对 Java 中的 修饰符,做一个微不足道的小小小小记 Java 语言提供了很多修饰符,大概分为两类: 访问权限修饰符 非访问权限修饰符 访问权限修饰符 修饰符 说明 publi ...
- Java:Object对象小记
Java:Object对象小记 对 Java 中的 Object 对象,做一个微不足道的小小小小记 Object 的常用方法有哪些 clone() 方法:用于创建并返回当前对象的一份拷贝: 在Java ...
- 热身训练1 Blood Cousins Return
点此看题 简要题面: 一棵树上有n个节点,每个节点有对应的名字(名字可重复). 每次询问,求深度比$vi$多$ki$的$vi$的儿子中,有多少种名字 分析: Step1: 我们可以懂$DFS$轻松找到 ...
- [NOIP模拟46]鼠树
神仙题. 首先不考虑把黑点变白,发现每个白点的信息与它的归属点是相同的.可以在线段树中只维护黑点的信息,再记录$DFS$序上每个点之前黑点个数的前缀和,每次操作可以二分出该点的归属点进行操作. 具体维 ...
- Machine learning (6-Logistic Regression)
1.Classification However, 2.Hypothesis Representation Python code: import numpy as np def sigmoid(z) ...
- Django(71)图片处理器django-imagekit
介绍 ImageKit是用于处理图像的Django应用程序.如果需要从原图上生成一个长宽为50x50的图像,则需要ImageKit. ImageKit附带了一系列图像处理器,用于调整大小和裁剪等常见任 ...