写在前面

经过前面的学习,我们了解了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. android 数据绑定(4)实用特性及疑惑:使用控件、格式化@string/xxx、对象传递、双向数据绑定

    1.在布局内使用其它控件 1.1 效果 箭头所指3个控件的内容随输入框内容而变化. 1.2 示例代码 <?xml version="1.0" encoding="u ...

  2. Macos 编译运行调试Mysql源代码

    准备编译工具Clion 下载地址 工具是macos用的系统 百度云盘下载地址(密码: 7dus) 下载mysql源码 Mysql源码下载地址 下载boost boost下载地址 前期准备工作 MySQ ...

  3. postgres 无法删除表

    起因 在postgress下删除表的时候报错 解决 简单的百度了一下,有些人说是用户权限的问题,需要切换到库的拥有者下删除,但是切换后还是没有解决··· 最后换了一种方式搜索,不直接搜索报错命令,直接 ...

  4. Java实现获取命令行中获取指定数据

    执行ipconfig /all获取主机所有网卡信息并分析这些字符串,提取出有效网卡(网卡名称,mac地址,ipv4地址,掩码,网关,dns)将网卡插入HashMap中,key是网卡的名称,value是 ...

  5. visual studio项目多级引用不拷贝dll的问题

    最近碰到一个visual studio项目多级引用不拷贝dll的问题,花了很久查了很多资料,特此记录 A项目引用B项目, B项目引用C项目,C项目引用ef及oracle.ef using Oracle ...

  6. [程序员代码面试指南]递归和动态规划-排成一条线的纸牌博弈问题(DP)

    题目 给定一个整型数组arr,代表数值不同的纸牌排成一条线.玩家A和玩家B依次拿走每张纸牌,规定玩家A先拿,玩家B后拿,但是每个玩家每次只能拿走最左或最右的纸牌,玩家A和玩家B都绝顶聪明.请返回最后获 ...

  7. MySQL安装错误Couldn't find MySQL server

    Starting MySQL ERROR! Couldn't find MySQL server (/usr/local/mysql/bin/mysqld_safe) 昨天rpm安装MySQL5.7后 ...

  8. 一篇文章说清楚TDengine的FQDN

    TDengine2.0以后需要使用FQDN来进行访问.小朋友,你是否有很多小问号:什么是FQDN,为什么要配置FQDN,如何配置FQDN.我们今天来简单讲一下.心急的小伙伴,可以直接跳转到配置章节. ...

  9. VC 编译 MATLAB 的 mex 文件

    VC 编译 MATLAB 的 mex 文件mex 文件是 MATLAB 调用其他程序设计语言程序或算法的接口.在 Windows 环境中,mex 文件是扩展文件名为 DLL 的动态链接库,可以在 m ...

  10. spring mvc(5) HandlerAdapter

    前面我们讲到了通过HandlerMapping可以获得不同类型的处理器,可以是Controller.HttpRequestHandler.Servlet.HandlerMethod甚至是我们自定义的处 ...