写在前面

经过前面的学习,我们了解了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. MySql密码的问题

    由于长时间没使用过MySql了,也由于之前没有做笔记的习惯,晚上因为MySQL的密码问题导致数据库长时间没连上.纠结了这么久还是决定记录下来,毕竟安装的东西多了,这年头到处都是密码,加上时间一长,很容 ...

  2. linux安装dubbo与zookeeper(一)

    所需工具: jdk1_7.tar.gz dubbo-admin-2.5.4.war(此文件不需解压) zookeeper.tar.gz tomcat7.0.tar.gz 以上文件下载需根据自己的电脑系 ...

  3. C++11中std::move、std::forward、左右值引用、移动构造函数的测试

    关于C++11新特性之std::move.std::forward.左右值引用网上资料已经很多了,我主要针对测试性能做一个测试,梳理一下这些逻辑,首先,左值比较熟悉,右值就是临时变量,意味着使用一次就 ...

  4. Python 零基础快速入门!

    “人生苦短,我学python”是编程届的名言.用python写小脚本的便捷性,让很多其他语言的学习者把python当作辅助语言.拥有了某一个语言的功底,再来学习另外一种语言应该是十分快速的.编程理念都 ...

  5. Unit2:活动

    1.基本用法 1.创建活动 Generate LayoutFile 创建布局文件 Launcher Activity 自动注册为主活动 编写顺序 活动Activity 注册.xml 界面res.xx ...

  6. PHP7性能提升原因

    1.存储变量的结构体变小,尽量使结构体里成员共用内存空间,减少引用,这样内存占用降低,变量的操作速度得到提升 2.字符串结构体的改变,字符串信息和数据本身原来是分成两个独立内存块存放,php7尽量将它 ...

  7. [LeetCode]364. 加权嵌套序列和 II (DFS)

    题目 给一个嵌套整数序列,请你返回每个数字在序列中的加权和,它们的权重由它们的深度决定. 序列中的每一个元素要么是一个整数,要么是一个序列(这个序列中的每个元素也同样是整数或序列). 与 前一个问题 ...

  8. [POJ]Find The Multiple(DFS)

    题目链接 http://poj.org/problem?id=1426 题意 输入一个数n,输出任意一个 只含0.1且能被n整除的数m.保证n<=200,m最多100位. 题解 DFS/BFS都 ...

  9. 从CSDN博客下载的图片如何无损去水印

    如果你想下载别人CSDN博客文章中很好看的图片,但却有水印 想要下载去水印的图片,可以先鼠标右击该图片,选择复制图片地址 https://img-blog.csdnimg.cn/20200916140 ...

  10. java 并发线程池的理解和使用

    一.为什么要用线程池 合理利用线程池能够带来三个好处. 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 第二:提高响应速度.当任务到达时,任务可以不需要的等到线程创建就能立 ...