写在前面

经过前面的学习,我们了解了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 studio生成aar包

    android studio生成aar包并在其他工程引用aar包 http://blog.csdn.net/getchance/article/details/47257389 用Android st ...

  2. C#转PHP

    官方主页 https://github.com/isukces/cs2php 快速开始 http://www.cs2php.com/how-to-begin.htm#.W2rBhC2B3mI 如何在V ...

  3. 线上环境去除console

    npm i -D babel-plugin-transform-remove-console babel.config.js // 获取 VUE_APP_ENV 非 NODE_ENV,测试环境依然 c ...

  4. [bash] 打印到屏幕相关语法

    程序: #!/bin/bash function showAlertMsg(){ echo -e "\e[1;31m"$"\e[0m" } function s ...

  5. 字符串split的用法

    拆分字符串:张三:20|李四:40|王五:40 这个可以使用两次分割,第一次使用 | 分割,放到arr数组里,然后使用循环对arr[i]进行使用:分割 public static void main( ...

  6. 关于Nginx mmap(MAP_ANON|MAP_SHARED, 314572800)报错

    mmap 报错解决 今天修改了一下测试环境的Nginx的nginx.conf,然后做检测的时候报了一个错误 /usr/local/bin/nginx -c /usr/local/etc/openres ...

  7. AD16

    第三集   制作光敏小夜灯的原理图 1.点击G切换栅格的精度 2.元器件放置好之后要先布局在布线 3.布线完成后要检查电路的合理性.对应查一下电阻的个数,位置是不是符合.在原理上大概的估计是否可以. ...

  8. Django进入监听端口就自动打开指定页面,无需导航栏手动添加(Django六)

    在我们进入监听端口时画面如下:而因为在urls.py中写过如下语句 我们在监听端口后加上/login就会跳转到login.html页面,如下图 那么如何一打开监听端口就可以走动跳转到login.htm ...

  9. python之cookie与session

    cookie概念 cookie不属于http协议范围,由于http协议无法保持状态,但实际情况,我们却又需要“保持状态”,因此cookie就是在这样一个场景下诞生. cookie的工作原理是:由服务器 ...

  10. ajax之---上传文件

    “伪”ajax向后台提交文件        <iframe style="display: none" id="iframe1" name="i ...