写在前面

经过前面的学习,我们了解了shiro中的认证流程,并且学会了如何通过自定义Realm实现应用程序的用户认证。在这篇文章当中,我们将学习shiro中的授权流程。

授权概述

这里的授权指的是授予某一系统的某一用户访问受保护资源的权限,分为查询、修改、插入和删除几类。没有相关权限的用户将无法访问受保护资源,具有权限的用户只能在自己权限范围内操作受保护资源。

关键对象

主体(Subject)

即指定的某一用户,这里的用户可以是浏览器、APP和第三方应用程序等。

资源(Resource)

这里的资源包括资源本身和对资源的操作。资源本身具备资源类型和资源实例两个属性。用户信息就是一个资源类型,向南的具体信息就是用户信息的实例。资源操作主要由查询、修改、删除、添加等组成。

权限(Permission)

权限即是主体操作资源所需要的许可。权限本身不存在,只是某一系统的受保护资源的访问标识。脱离了资源谈权限就没有了意义。

授权流程(访问控制流程)

授权流程分析

​ 前提:访问主体已经通过认证,登录到系统中。

  1. 用户请求访问某一受保护资源

  2. 判断用户是否具备访问权限:

    2.1 有,则执行步骤3

    2.2 没有,拒绝用户访问,并返回相应的提示

  3. 用户访问资源

授权模型

目前面向民用系统主流的授权模式主要有基于资源的的访问控制和基于角色的访问控制

  1. 基于角色的访问控制RBAC (Role-Based Access Control)

    其基本思想是,对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立一个角色集合。每一种角色对应一组相应的权限。一旦用户被分配了适当的角色后,该用户就拥有此角色的所有操作权限。

  2. 基于资源的访问控制RBAC(Resource-Based Access Control )

    在基于角色的访问控制当中,一但用户的角色确定了,那么,其权限也就被固定下来了。也就是说具有相同角色的两个主体,他们的权限是一样的。没有办法做到动态地改变主体的权限。基于资源的访问控制就是为了解决这个问题。

权限字符串

权限字符串由资源标识符、操作符、资源实例标识符和分隔符组成,格式:资源标识符:操作符:资源实例标识符 。其含义是:对指定的一个或多个资源实例具有指定的操作权限,当资源实例为任意个时,资源实例标识符用*表示。分隔符:也可以换成/等形式。操作符一般分为createfindupdatedelete四类,当具备该资源的所有操作时,操作符也可以用*表示。当资源标识符、操作符、资源实例标识符都为*时,代表该用户具备该系统的所有资源访问权限。

一些例子

  1. 对用户01的查询权限表示为:user:find:01
  2. 对用户具有查询权限表示为:user:find:*或者user:find
  3. 对用户01具有所有权限表示为:user:*:01

上面只是列出了一种权限字符串的设计方法,大家在实践当中可以根据自己的应用特点去设计自己的权限字符串。

shiro中的授权(访问控制)实现方式

编程式

Subject currentSubject = SecurityUtils.getSubject();
if (currentSubject.hasRole("admin")){
//有权限的操作
}else{
//无权限
}

注解式(常用)

@RequiresRoles("admin")
public boolean createUser(User user){
//有权限才能执行方法
}

标签式

//在jsp中
<shiro:hasRole name="admin">
//有权限才展示
</shiro:hasRole>

注意 :在Thymeleaf中使用时需要额外的集成。

编程实现

我们沿用上一篇文章当中的例子,来演示shiro授权部分的内容。文末附有本文例子的下载方式

基于角色的访问控制

改造自定义Realm获取角色信息

**自定义Realm对象
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/4 11:00
*/
public class MySqlRealm extends AuthorizingRealm { public MySqlRealm() {
//设置凭证匹配器,修改为hash凭证匹配器
HashedCredentialsMatcher myCredentialsMatcher = new HashedCredentialsMatcher();
//设置算法
myCredentialsMatcher.setHashAlgorithmName("md5");
//散列次数
myCredentialsMatcher.setHashIterations(1024);
this.setCredentialsMatcher(myCredentialsMatcher);
} /**授权方法
* 对于授权方法,每次判断主体是否具备对应权限时都会调用
* 因此,这里应当做缓存
* 缓存会在后面与springboot整合时讲
* @author 赖柄沣 bingfengdev@aliyun.com
* @date 2020-10-04 11:01:50
* @param principalCollection
* @return org.apache.shiro.authz.AuthorizationInfo
* @throws AuthenticationException
* @version 1.0
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //1. 获取当前主体的主身份信息,即用户名
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
//2. 根据主身份信息查询数据库,获取主体具备的权限(模拟)
SimpleAuthorizationInfo authenticationInfo = null;
if ("xiangbei".equals(primaryPrincipal)){
authenticationInfo = new SimpleAuthorizationInfo();
authenticationInfo.addRole("admin");
}
return authenticationInfo;
} /**认证
* @author 赖柄沣 bingfengdev@aliyun.com
* @date 2020-10-04 11:01:50
* @param authenticationToken
* @return org.apache.shiro.authz.AuthorizationInfo
* @throws AuthenticationException
* @version 1.0
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 1. 从token中获取用户名
String principal = (String) authenticationToken.getPrincipal(); //2. 根据用户名查询数据库并封装成authenticationinfo对象返回(模拟)
if (principal == "xiangbei") {
//四个参数分别是数据库中的账号、加密后的密码、盐值、realm名字
AuthenticationInfo authInfo = new SimpleAuthenticationInfo("xiangbei",
"ff595c47b51b4cf70fddce090f68879e",
ByteSource.Util.bytes("ee575f62-0dda-44f2-b75e-4efef795018f"),
this.getName());
return authInfo;
} return null;
}
}

进行测试

hasRole
/**访问控制测试
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/5 16:48
*/
public class AuthzTest {
private CurrentSystemAuthenticator authenticator;
@Before
public void init() {
this.authenticator = new CurrentSystemAuthenticator();
//对于授权,只有主体通过认证后才能进行,所以需要先登录系统
this.authenticator.authenticate("xiangbei","123");
} @Test
public void testHasRole(){
Subject subject = SecurityUtils.getSubject();
//主体具备某一角色即可访问
if (subject.hasRole("admin")){
System.out.println(subject.getPrincipal()+" 具有 admin 角色");
} } }

输出

xiangbei 认证通过!
xiangbei 具有 admin 角色
hasRoles
**访问控制测试
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/5 16:48
*/
public class AuthzTest {
private CurrentSystemAuthenticator authenticator;
@Before
public void init() {
this.authenticator = new CurrentSystemAuthenticator();
//对于授权,只有主体通过认证后才能进行,所以需要先登录系统
this.authenticator.authenticate("xiangbei","123");
} /**
适用于只要有其中一个角色即可的情况
*/
@Test
public void testHasRoles(){
Subject subject = SecurityUtils.getSubject();
boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user"));
for (boolean b : booleans) {
if (b) {
System.out.println(subject.getPrincipal()+" 具有访问权限");
break;
}
} } }

输出

xiangbei 认证通过!
xiangbei 具有访问权限
hasAllRoles
/**访问控制测试
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/5 16:48
*/
public class AuthzTest {
private CurrentSystemAuthenticator authenticator;
@Before
public void init() {
this.authenticator = new CurrentSystemAuthenticator();
//对于授权,只有主体通过认证后才能进行,所以需要先登录系统
this.authenticator.authenticate("xiangbei","123");
} /**
具备所有角色才能访问
*/
@Test
public void testHasAllRoles(){
Subject subject = SecurityUtils.getSubject();
boolean b = subject.hasAllRoles(Arrays.asList("admin", "user"));
if (b) {
System.out.println(subject.getPrincipal()+" 具有访问权限");
}else {
System.out.println(subject.getPrincipal()+" 没有访问权限");
}
}
}

输出

xiangbei 认证通过!
xiangbei 没有访问权限

基于资源的访问控制

改造自定义Realm获取资源权限信息

/**自定义Realm对象
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/4 11:00
*/
public class MySqlRealm extends AuthorizingRealm { public MySqlRealm() {
//设置凭证匹配器,修改为hash凭证匹配器
HashedCredentialsMatcher myCredentialsMatcher = new HashedCredentialsMatcher();
//设置算法
myCredentialsMatcher.setHashAlgorithmName("md5");
//散列次数
myCredentialsMatcher.setHashIterations(1024);
this.setCredentialsMatcher(myCredentialsMatcher);
} /**授权方法
* 对于授权方法,每次判断主体是否具备对应权限时都会调用
* 因此,这里应当做缓存
* 缓存会在后面与springboot整合时讲
* @author 赖柄沣 bingfengdev@aliyun.com
* @date 2020-10-04 11:01:50
* @param principalCollection
* @return org.apache.shiro.authz.AuthorizationInfo
* @throws AuthenticationException
* @version 1.0
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //1. 获取当前主体的主身份信息,即用户名
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
//2. 根据主身份信息查询数据库,获取主体具备的权限(模拟)
SimpleAuthorizationInfo authenticationInfo = null;
if ("xiangbei".equals(primaryPrincipal)){
authenticationInfo = new SimpleAuthorizationInfo();
//authenticationInfo.addRole("admin"); //具备user的所有权限
authenticationInfo.addStringPermission("user:*"); //具备产品的创建权限
authenticationInfo.addStringPermission("product:create");
}
return authenticationInfo;
} /**认证
* @author 赖柄沣 bingfengdev@aliyun.com
* @date 2020-10-04 11:01:50
* @param authenticationToken
* @return org.apache.shiro.authz.AuthorizationInfo
* @throws AuthenticationException
* @version 1.0
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 1. 从token中获取用户名
String principal = (String) authenticationToken.getPrincipal(); //2. 根据用户名查询数据库并封装成authenticationinfo对象返回(模拟)
if (principal == "xiangbei") {
//四个参数分别是数据库中的账号、加密后的密码、盐值、realm名字
AuthenticationInfo authInfo = new SimpleAuthenticationInfo("xiangbei",
"ff595c47b51b4cf70fddce090f68879e",
ByteSource.Util.bytes("ee575f62-0dda-44f2-b75e-4efef795018f"),
this.getName());
return authInfo;
} return null;
}
}

进行测试

在上面的设定中,用户xiangbei具有用户资源的所有权限,对产品具有创建权限

isPermitted(String permission)

具备某一具体资源权限才能访问

/**
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/5 17:23
*/
public class AuthzTest2 {
private CurrentSystemAuthenticator authenticator;
@Before
public void init() {
this.authenticator = new CurrentSystemAuthenticator();
//对于授权,只有主体通过认证后才能进行,所以需要先登录系统
this.authenticator.authenticate("xiangbei","123");
} @Test
public void testIsPermission() {
Subject subject = SecurityUtils.getSubject();
if (subject.isPermitted("user:create")){
System.out.println(subject.getPrincipal() + " 具有 用户创建权限"); }
}
}

输出

xiangbei 认证通过!
xiangbei 具有 用户创建权限
isPermitted(String... permission)或者isPermitted(List permissions)

适用于要求具备某些权限的其中一部分的情况

/**
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/5 17:23
*/
public class AuthzTest2 {
private CurrentSystemAuthenticator authenticator;
@Before
public void init() {
this.authenticator = new CurrentSystemAuthenticator();
//对于授权,只有主体通过认证后才能进行,所以需要先登录系统
this.authenticator.authenticate("xiangbei","123");
}
@Test
public void testIsPermitted(){
Subject subject = SecurityUtils.getSubject();
boolean[] permitted = subject.isPermitted("user:create", "user:find");
for (boolean b : permitted) {
if (b) {
System.out.println(subject.getPrincipal() +" 具有访问权限");
break;
}
}
}
}
isPermittedAll

这个适用于需要主体具备所列出的所有资源才能访问的情况

/**
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/5 17:23
*/
public class AuthzTest2 {
private CurrentSystemAuthenticator authenticator;
@Before
public void init() {
this.authenticator = new CurrentSystemAuthenticator();
//对于授权,只有主体通过认证后才能进行,所以需要先登录系统
this.authenticator.authenticate("xiangbei","123");
} @Test
public void testIsPermittedAll() {
Subject subject = SecurityUtils.getSubject();
boolean b = subject.isPermittedAll("product:*");
if (b) {
System.out.println(subject.getPrincipal() +" 具备 product 模块的所有权限");
}else {
System.out.println(subject.getPrincipal() +" 没有 product 模块的访问权限");
}
}

输出

xiangbei 认证通过!
xiangbei 没有 product 模块的访问权限

写在后面

在这篇文章当中,我们主要学习了shrio的访问控制,并通过一个简单的例子给大家做了演示。在下一篇Shiro系列的文章当中,作者将介绍SpringBoot整合Shiro的相关内容。

如果您觉得这篇文章能给您带来帮助,那么可以点赞鼓励一下。如有错误之处,还请不吝赐教。在此,谢过各位乡亲父老!

本文例子下载方式: 微信搜索【Java开发实践】,回复20201005 即可得到下载链接。

shiro入门学习--授权(Authorization)|筑基初期的更多相关文章

  1. Shiro入门学习之shi.ini实现授权(三)

    一.Shiro授权 前提:需要认证通过才会有授权一说 1.授权过程 2.相关方法说明 ①subject.hasRole("role1"):判断是否有该角色 ②subject.has ...

  2. Shiro入门学习与实战(一)

    一.概述 1.Shiro是什么? Apache Shiro是java 的一个安全框架,主要提供:认证.授权.加密.会话管理.与Web集成.缓存等功能,其不依赖于Spring即可使用: Spring S ...

  3. shiro入门学习--使用MD5和salt进行加密|练气后期

    写在前面 在上一篇文章<Shiro入门学习---使用自定义Realm完成认证|练气中期>当中,我们学会了使用自定义Realm实现shiro数据源的切换,我们可以切换成从关系数据库如MySQ ...

  4. Shiro入门学习之自定义Realm实现授权(五)

    一.自定义Realm授权 前提:认证通过,查看Realm接口的继承关系结构图如下,要想通过自定义的Realm实现授权,只需继承AuthorizingRealm并重写方法即可 二.实现过程 1.新建mo ...

  5. Shiro入门学习之散列算法与凭证配置(六)

    一.散列算法概述 散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,常见的散列算法如MD5.SHA等,一般进行散列时最好提供一个salt(“盐”),什么意思?举个栗子 ...

  6. Shiro入门学习---使用自定义Realm完成认证|练气中期

    写在前面 在上一篇文章<shiro认证流程源码分析--练气初期>当中,我们简单分析了一下shiro的认证流程.不难发现,如果我们需要使用其他数据源的信息完成认证操作,我们需要自定义Real ...

  7. Shiro入门学习之自定义Realm实现认证(四)

    一.概述 Shirom默认使用自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,而大部分情况下需要从系统数据库中读取用户信息,所以需要实现自定义Realm,Realm接口如下: ...

  8. Shiro入门学习之shi.ini实现认证及源码分析(二)

    一.Shiro.ini文件 1.文件说明 ①ini(InitializationFile)初始文件:Window系统文件扩展名 ②Shiro使用时可以连接数据库,也可以不连接数据库(可以使用shiro ...

  9. Shiro learning - 入门学习 Shiro中的基础知识(1)

    Shiro入门学习 一 .什么是Shiro? 看一下官网对于 what is Shiro ? 的解释 Apache Shiro (pronounced “shee-roh”, the Japanese ...

随机推荐

  1. TP6.0 一对一模型关联 belongsTo 相对关联(反向关联)

    1. 创建数据表 一对一反向关联使用率很高 附表关联主表称为反向关联,又称为相对关联(tp官方手册这样叫) -- 分类表 CREATE TABLE `category` ( `id` int(10) ...

  2. 说说ERP软件的系统设计--开源软件诞生8

    赤龙ERP系统设计篇--第8篇 用日志记录"开源软件"的诞生 赤龙 ERP 开源地址: 点亮星标,感谢支持,与开发者交流 kzca2000 码云:https://gitee.com ...

  3. 如何编写一个简单的Linux驱动(二)——设备操作集file_operations

    前期知识 如何编写一个简单的Linux驱动(一)--驱动的基本框架 前言 在上一篇文章中,我们学习了驱动的基本框架.这一章,我们会在上一章代码的基础上,继续对驱动的框架进行完善.要下载上一篇文章的全部 ...

  4. 用c语言处理文件

    用c语言处理文件只需要用到几个简单的函数: 1.文件的打开和关闭 fopen()函数用来打开一个文件,该函数原型在头文件stdio.h中,调用的一般形式为 /* FILE 是c语言内置的一个结构体类型 ...

  5. 猜数字 python 3

    随机选取一个1-100的整数 通过5次机会猜整数 会提醒猜大或者猜小 当猜对或者已经猜过5次后结束游戏

  6. PHP之道(PHP The Right Way)

    原文地址:http://laravel-china.github.io/php-the-right-way/

  7. [LeetCode]494. 目标和、416. 分割等和子集(0-1背包,DP)

    题目一 494. 目标和 给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S.现在你有两个符号 + 和 -.对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前 ...

  8. hadoop文件系统上的小文件合并-Hadoop Archives

    1. 什么是Hadoop archives Hadoop archives是特殊的档案格式.一个Hadoop archive对应一个文件系统目录. Hadoop archive的扩展名是.har.Ha ...

  9. 学习 | mockjs入门

    最近工作有用到mockjs模拟数据,实现前后端分离,今天系统的把mockjs的API都过了一遍,算是入门吧. 什么是mockjs mockjs就是一个模拟数据,生成随机数据,拦截ajax请求. 为什么 ...

  10. 用华为HMS ML kit人体骨骼识别技术,Android快速实现人体姿势动作抓拍&nbsp;

      你有没有过这种体验,拍照时对着镜头,脑子一片空白.表情僵硬.手和脚无处安放,最后拍出来的照片很是奇怪.拍照软件中的固定姿势抓拍功能可以帮助你:选择一个你想要的姿势模板,当你摆出同款姿势时,软件会进 ...