Shiro中的授权问题
在初识Shiro一文中,我们对Shiro的基本使用已经做了简单的介绍,不懂的小伙伴们可以先阅读上文,今天我们就来看看Shiro中的授权问题。
Shiro中的授权,大体上可以分为两大类,一类是隐式角色,还有一类是显式角色。我们来分别看下。
隐式角色
隐式角色是一种基于角色的访问权限控制,它在使用的过程中,我们直接判断相应的Subject是否是某一种角色,进而判断该Subject是否具备某种权限,比如下面一个例子:
定义用户
在ini文件中定义用户和对应的角色:
[users]
zhang=123,role1,role2
wang=123,role1
两个用户,zhang用户具有role1、role2两种角色,wang用户具有role1一种角色。
判断用户是否具备某种角色
先来个方法用来执行登录操作,如下:
private void login(String iniFile, String username, String password) {
Factory<SecurityManager> factory = new IniSecurityManagerFactory(iniFile);
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
System.out.println("登录成功");
} catch (AuthenticationException e) {
System.out.println("登录失败");
e.printStackTrace();
}
}
这个登录比较简单,我们在上文中有详细介绍。 OK,我们看看如何测试:
@Test
public void test1() {
login("classpath:shiro.ini", "zhang", "123");
Subject subject = SecurityUtils.getSubject();
//判断是否具备某一种权限
Assert.assertTrue(subject.hasRole("role1"));//OK
Assert.assertTrue(subject.hasRole("role2"));//OK
List<String> roles = new ArrayList<String>();
roles.add("role1");
roles.add("role2");
roles.add("role3");
//判断是否具备某一组权限
Assert.assertArrayEquals(new boolean[]{true, true, false}, subject.hasRoles(roles));//OK
}
OK,上文的测试结果都是OK的,我们用hasRole方法来判断某一个Subject是否具备某一种权限,用hasRoles方法来判断某一个Subject是否具备某一组权限。 可能有的小伙伴已经发现了这种方法的弊端,如果有一天我不需要这些判断了该怎么办?一个一个修改?太可怕了!so,我们来看看下文的显示角色是怎么一回事。
显式角色
显式角色是一种基于资源的访问权限控制,使用显式角色可以避免上文提到的问题,但是使用显式角色又需要我们自己手动维护用户-角色、角色-权限之间的关系。 OK ,我们先来看一个简单的案例。
单个资源具备多个权限
在ini文件中定义两个用户:
[users]
zhang = 123,role1,role2
wang = 123,role1
两个用户具备两种角色,然后再定义这两种角色对应的权限,权限的定义格式一般是资源:操作:
[roles]
#单个资源多个权限
role1 = user:create,user:update
role2 = user:create,user:delete
role1和role2分别具备两种不同的权限。 我们来看看测试
@Test
public void test2() {
login("classpath:shiro-res.ini", "wang", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("user:create"));//OK
Assert.assertFalse(subject.isPermittedAll("user:delete", "user:update"));//OK
Assert.assertFalse(subject.isPermitted("user:view"));//OK
}
以上测试结果都是OK的,wang这个用户具备user:create权限,但是它不同时具备user:delete和user:update权限,同时它也不具有user:view权限。
OK,单个资源具备多个权限除了上面的定义方式之外,我们也可以按下面的方式来定义:
#单个资源的多个权限
role3 = system:user:update,system:user:delete
#role3可以简写为下面这种形式
role31 = "system:user:update,delete"
注意,第二种写法要加引号,这两种都是表示相应的角色具备update和delete权限。测试方法如下:
@Test
public void role3Test() {
login("classpath:shiro-res.ini", "user_role3", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("system:user:update"));//OK
Assert.assertTrue(subject.isPermitted("system:user:delete"));//OK
}
@Test
public void role31Test() {
login("classpath:shiro-res.ini", "user_role31", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("system:user:update"));//OK
Assert.assertTrue(subject.isPermitted("system:user:delete"));//OK
Assert.assertTrue(subject.isPermitted("system:user:update,delete"));//OK
}
这里的测试结果都是OK的。
单个资源具有全部权限
ini配置如下:
#单个资源全部权限
role4 = "system:user:update,create,delete,view"
#role4可以简写为下面的形式
role41 = system:user:*
#role4也可以简写为下面的形式
role42 = system:user
注意这种有两种不同的简写方式,一种是用*作为通配符,还有一种就是干脆省略。 我们来看看测试方式:
@Test
public void role4Test() {
login("classpath:shiro-res.ini", "user_role4", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("system:user:update"));//OK
Assert.assertTrue(subject.isPermitted("system:user:delete"));//OK
Assert.assertTrue(subject.isPermitted("system:user:view"));//OK
Assert.assertTrue(subject.isPermitted("system:user:create"));//OK
Assert.assertTrue(subject.isPermitted("system:user:update,delete,view,create"));//OK
Assert.assertTrue(subject.isPermitted("system:user:update,delete,create"));//OK
}
@Test
public void role41Test() {
login("classpath:shiro-res.ini", "user_role41", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("system:user:update"));//OK
Assert.assertTrue(subject.isPermitted("system:user:delete"));//OK
Assert.assertTrue(subject.isPermitted("system:user:view"));//OK
Assert.assertTrue(subject.isPermitted("system:user:create"));//OK
Assert.assertTrue(subject.isPermitted("system:user:update,delete,view,create"));//OK
Assert.assertTrue(subject.isPermitted("system:user:update,delete,create"));//OK
Assert.assertTrue(subject.isPermitted("system:user:*"));//OK
Assert.assertTrue(subject.isPermitted("system:user"));//OK
}
@Test
public void role42Test() {
login("classpath:shiro-res.ini", "user_role42", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("system:user:update"));//OK
Assert.assertTrue(subject.isPermitted("system:user:delete"));//OK
Assert.assertTrue(subject.isPermitted("system:user:view"));//OK
Assert.assertTrue(subject.isPermitted("system:user:create"));//OK
Assert.assertTrue(subject.isPermitted("system:user:update,delete,view,create"));//OK
Assert.assertTrue(subject.isPermitted("system:user:update,delete,create"));//OK
Assert.assertTrue(subject.isPermitted("system:user:*"));//OK
Assert.assertTrue(subject.isPermitted("system:user"));//OK
}
OK,以上三个方法分别测试三个不同的权限定义方式,结果都是OK的。
所有资源具有全部权限
当然我们也可以定义所有资源具有全部权限,定义方式如下:
#所有资源的全部权限 user:view
role5 = *:view
#system:user:view
role51 = *:*:view
@Test
public void role5Test() {
login("classpath:shiro-res.ini", "user_role5", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("user:view"));//OK
}
@Test
public void role51Test() {
login("classpath:shiro-res.ini", "user_role51", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("system:user:view"));//OK
}
两个测试结果都是OK的。
实例级别的权限
我们的权限定义也可以具体到实例级别,如下:
#单个实例单个权限
#对user的1拥有view权限
role6 = user:view:1
#单个实例多个权限
role61 = "user:update,delete:1"
#单个实例的所有权限
role62 = user:*:1
#所有实例的单个权限
role63 = user:view:*
#所有实例的所有权限
role64 = user:*:*
测试代码如下:
@Test
public void role6Test() {
login("classpath:shiro-res.ini", "user_role6", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("user:view:1"));//OK
}
@Test
public void role61Test() {
login("classpath:shiro-res.ini", "user_role61", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("user:update:1"));//OK
Assert.assertTrue(subject.isPermitted("user:delete:1"));//OK
Assert.assertTrue(subject.isPermitted("user:update,delete:1"));//OK
}
@Test
public void role62Test() {
login("classpath:shiro-res.ini", "user_role62", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("user:update:1"));//OK
Assert.assertTrue(subject.isPermitted("user:delete:1"));//OK
Assert.assertTrue(subject.isPermitted("user:view:1"));//OK
Assert.assertTrue(subject.isPermitted("user:create:1"));//OK
Assert.assertTrue(subject.isPermitted("user:create,view,update,delete:1"));//OK
Assert.assertTrue(subject.isPermitted("user:update,delete:1"));//OK
}
@Test
public void role63Test() {
login("classpath:shiro-res.ini", "user_role63", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("user:view:1"));//OK
Assert.assertTrue(subject.isPermitted("user:view:2"));//OK
Assert.assertTrue(subject.isPermitted("user:view:3"));//OK
Assert.assertTrue(subject.isPermitted("user:view:4"));//OK
}
@Test
public void role64Test() {
login("classpath:shiro-res.ini", "user_role64", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("user:view:1"));//OK
Assert.assertTrue(subject.isPermitted("user:update:2"));//OK
Assert.assertTrue(subject.isPermitted("user:create:3"));//OK
Assert.assertTrue(subject.isPermitted("user:delete:4"));//OK
}
测试结果都是OK的。
其他
OK,关于授权,我们这里还有两个问题,前文我们说的system:user
等价于system:user:*
,而对于system
,它除了等价于system:*
,也等价于system:*:*
,所以,我们可以把system:user
或者system
理解成一种前缀匹配。同时,我们用user:*
可以匹配user:update
、user:delete
等,而user:delete
又可以匹配user:delete:1
,因此我们可以这样理解:不加*
,我们可以做前缀匹配,加*
,我们则可以匹配所有。
OK,以上就是Shiro中简单的授权问题。
案例下载: https://github.com/lenve/Shiro/tree/master/Shiro2
参考资料: 张开涛大神的《跟我学Shiro》,原文连接http://jinnianshilongnian.iteye.com/blog/2018398
- finecms指定从第几篇文章开始调用5条记录,并调用文章所在栏目
- finecms如何调用多个栏目的子栏目
- 从傅立叶变换到Gabor滤波器
- 三个小时学会wordpress模板制作
- The each() function is deprecated报错的解决方法
- 书接上文:薛定谔的猫是如何诞生的?
- docker源码分析(3)---镜像(1)
- k8s源码分析-----kubelet(8)pod管理
- 大会 | DiracNets:无需跳层连接的ResNet
- golang时间戳格式化与解析
- golang-net/http源码分析之http server
- 白话面向智能体编程(Agent Oriented Programmig, AOP)之四
- 用fpm来做rpm打包
- Nginx配置文件nginx.conf详解
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Best Cow Line(POJ 3617)
- Flask单点登录竟然只要几行代码就能搞定!
- 区间调度问题
- 迷宫的最短路径
- Lake Counting (POJ No.2386)
- 部分和问题(DFS)
- 为什么 React Hooks useState 更新不符预期?
- 技术分享 | MySQL 使用 MariaDB 审计插件
- 第12期:压缩表性能监测
- 第05期:使用 prometheus 监控 clickhouse 集群
- 这 6 点知识让我对 JavaScript 的对象有了更进一步的了解
- Linux进程间通信(中)之信号、信号量实践
- Linux进程间通信(下)之共享内存实践
- 手把手教你快速使用Vmware虚拟机安装Linux操作系统实验环境
- 天啊!鹅厂都开始做开发板了?网红腾讯物联网开发板终极开箱评测,让我们一睹为快!