写在前面

经过前面的学习,我们了解了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. Unity Prefab关联

    Unity3D研究院之Prefab里面的Prefab关联问题http://www.xuanyusong.com/archives/3042

  2. List集合对象去重及按属性去重的8种方法-java基础总结系列第六篇

    最近在写一些关于java基础的文章,但是我又不想按照教科书的方式去写知识点的文章,因为意义不大.基础知识太多了,如何将这些知识归纳总结,总结出优缺点或者是使用场景才是对知识的升华.所以我更想把java ...

  3. 转载:MYSQL数据库三表联查的SQL优化过程

    地址:https://database.51cto.com/art/202002/609803.htm 作者用了三张有设计缺陷的表做例子,使得优化效果空前,优化手段仅为拨乱反正和加索引,此行可为一哂.

  4. 仅显示sessionid,servername,serverport的一个springboot小程序

    下载地址:https://files.cnblogs.com/files/xiandedanteng/sessionid20191227-1.zip --END-- 2019-12-2710:07

  5. Sunday算法解决字符串匹配问题

    概述 提起字符串匹配可能更多人会想到KMP算法,该算法时间复杂度为O(m+n),而且也是我们在学习数据结构过程中最早接触到的比较好的算法.但KMP算法需要在模式字符串有关联的情况下,也即模式字符串前后 ...

  6. node.js之koa安装

    默认安装了node 1.cmd中工作目录下输入npm init:一路回车即可: 2.还是在此目录下输入npm i koa:我这里是安装的淘宝镜像即是输入cpm i koa. 3.打开编辑器在文件目录下 ...

  7. postman -- 环境变量、全局变量使用

    背景: [登录接口]中会返回sign值,[学生金币充值接口]会则需要用到该sign值,因此把sign设置为环境或全局变量,便于其他接口调用. 1.请求登录接口,获取sign值: 2.把sign值添加至 ...

  8. IDEA 2020.2安装破解版教程激活码注册码破解补丁 for Mac Windows Linux-中关村老大爷

    IntelliJ Idea Ultimate 2020.2.x版软件最新安装破解版教程方法,本教程提供Idea Ultimate 2020.2版安装包,破解补丁,激活码,亲测100%完美破解激活,支持 ...

  9. 分布式系统监视zabbix讲解九之使用snmp监控windows

    zabbix监控windows演示 第一步:开启服务. 控制面板>程序>程序和功能>打开或关闭windows功能,勾选上简单网络管理协议(SNMP),以及下面的子程序.点击确定 第二 ...

  10. python文档翻译之使用python解释器

    Python解释器通常安装在/usr/local/bin/python3.6,把/usr/local/bin目录设置到UNIX shell的搜索路径就可以使用下面的命令运行python: python ...