Shiro中的授权问题(二)
上篇博客(Shiro中的授权问题 )我们介绍了Shiro中最最基本的授权问题,以及常见的权限字符的匹配问题。但是这里边还有许多细节需要我们继续介绍,本节我们就来看看Shiro中授权的一些细节问题。
验证流程
首先我们要搞明白整个的验证流程是什么样子的。 在上篇博客(Shiro中的授权问题 )中,我们验证Subject是否具备某项权限的时候使用的是isPermitted方法,但是在上上篇博客(初识Shiro )中,我们也说过,Subject只是Shiro中的一个门面而已,最终所有的操作都是委托给SecurityManager来处理的,那么这里也一样,我们调用了Subject的isPermitted方法,该方法会将验证操作委托给SecurityManager,SecurityManager又会将验证操作委托给Authorizer,Authorizer是我们这里真正的授权者,当我们调用isPermitted方法的时候,Authorizer首先会将我们传入的权限字符串转为相应的Permission实例,同时,系统也会调用Realm来获取Subject相应的角色或者权限,然后Authorizer会将我们传入的权限/角色和Realm中的权限/角色进行比对验证,而我们如果有多个Realm,则这个比对的操作又会被委托给ModularRealmAuthorizer进行循环判断,在判断的过程中,如果匹配成功就会返回true,否则返回false表示授权失败。
实例
OK,基于上文我们对授权过程的介绍,我们来自定义几个东西。验证一下我们上文的说法。在自定义之前,我们还是先来了解几个概念:Authorizer在Shiro中扮演的职责是授权,即访问控制,Authorizer提供了我们进行角色、权限判断时需要的接口等,我们常说的SecurityManager继承了Authorizer,还有一个类叫做PermissionResolver用于解析权限字符串到Permission实例,RolePermissionResolver则用于根据角色解析相应的权限集合,了解了这几个类的功能之后,我们来看看下面的自定义问题。
不知道小伙伴们对Linux中的权限机制是否有了解,Linux中用1、2、4
三个数字分别表示一个文件的可读可写可执行三种权限,如果想要文件获取多个权限,将对应的数字相加即可。OK,那么我们这里就参考这种模式,我来定义一个权限模型:
假设我用0表示所有权限,1表示create权限,2表示update权限,4表示delete权限,8表示view权限,然后在实际应用中,我用数字之和来表示资源所具备的权限,比如3表示该资源既有create权限又有update权限,10表示该资源既有update权限又有view权限,然后权限的定义以$
符号开始,资源和权限之间以$
符号分隔。OK,基于此,我们来看看如果自定义权限处理机制。
自定义权限策略
自定义Permission类实现Permission接口:
public class BitPermission implements Permission {
private String resourceIdentify;
private int permissionBit;
private String instanceId;
public BitPermission(String permissionString) {
String[] array = permissionString.split("$");
if (array.length > 1) {
resourceIdentify = array[1];
}
if (resourceIdentify == null || "".equals(resourceIdentify)) {
resourceIdentify = "*";
}
if (array.length > 2) {
permissionBit = Integer.valueOf(array[2]);
}
if (array.length > 3) {
instanceId = array[3];
}
if (instanceId == null || "".equals(instanceId)) {
instanceId = "*";
}
}
public boolean implies(Permission p) {
if (!(p instanceof BitPermission)) {
return false;
}
BitPermission bitPermission = (BitPermission) p;
if (!("*".equals(resourceIdentify) || this.resourceIdentify.equals(bitPermission.resourceIdentify))) {
return false;
}
if (!(this.permissionBit == 0 || (this.permissionBit & bitPermission.permissionBit) != 0)) {
return false;
}
if (!("*".equals(this.instanceId) || this.instanceId.equals(bitPermission.instanceId))) {
return false;
}
return true;
}
}
OK,这个是我们自定义的权限解析类,该类中有一个implies方法用来执行权限匹配操作,然后我们还需要提供了一个PermissionResolver类,该类将权限字符串解析为相应的类。如下:
public class BitAndWildPermissionResolver implements PermissionResolver {
public Permission resolvePermission(String permissionString) {
if (permissionString.startsWith("$")) {
return new BitPermission(permissionString);
}
return new WildcardPermission(permissionString);
}
}
我们在这里进行简单的判断,如果权限字符串是以$
开始的,我们使用自定义的BitPermission进行解析,如果是其他普通的权限字符串,我们就创建WildcardPermission即可。然后我们自定义需要的Realm,在Realm中定义权限,如下:
public class MyRealm extends AuthorizingRealm {
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addObjectPermission(new BitPermission("$user1$14"));
authorizationInfo.addStringPermission("$user2$10");
return authorizationInfo;
}
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
String password = new String(((char[]) token.getCredentials()));
if (!"wang".equals(username)) {
//用户名错误
throw new UnknownAccountException();
}
if (!"123".equals(password)) {
//密码错误
throw new IncorrectCredentialsException();
}
return new SimpleAuthenticationInfo(username, password, getName());
}
}
这里我定义了两种权限,一个是$user1$14
,表示了create(2),delete(4)和view(8)三种权限;还有一个是$user2$10
,表示了create(2)和view(8)两种权限。然后我们来看看在ini文件中如何配置:
[main]
authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
permissionResolver= org.sang.BitAndWildPermissionResolver
authorizer.permissionResolver=$permissionResolver
securityManager.authorizer=$authorizer
realm= org.sang.MyRealm
securityManager.realms=$realm
我们来看看测试代码:
@Test
public void test99() {
login("classpath:shiro-custom.ini", "wang", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("$user1$2"));//OK
Assert.assertTrue(subject.isPermitted("$user1$8"));//OK
Assert.assertTrue(subject.isPermitted("$user2$10"));//OK
Assert.assertTrue(subject.isPermitted("$user1$4"));//OK
}
测试结果如下: 绿色表示运行都是OK的。 OK,除了上面 这种自定义的形式之外,我们还可以自定义role解析,根据role的字符串解析出role中的权限集合。
自定义角色解析策略
我们也可以自己来设计角色解析策略,如下:
public class MyRolePermissionResolver implements RolePermissionResolver {
public Collection<Permission> resolvePermissionsInRole(String roleString) {
if ("role1".equals(roleString)) {
return Arrays.asList(((Permission) new WildcardPermission("menu:*")));
}
return null;
}
}
如果用户具有role1角色,那么我让他具有menu:*权限,OK,然后我在Realm中增加两个权限,新的Realm类如下:
public class MyRealm extends AuthorizingRealm {
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addRole("role1");
authorizationInfo.addRole("role2");
authorizationInfo.addObjectPermission(new BitPermission("$user1$14"));
authorizationInfo.addObjectPermission(new WildcardPermission("user1:*"));
authorizationInfo.addStringPermission("$user2$10");
authorizationInfo.addStringPermission("user2:*");
return authorizationInfo;
}
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
String password = new String(((char[]) token.getCredentials()));
if (!"wang".equals(username)) {
//用户名错误
throw new UnknownAccountException();
}
if (!"123".equals(password)) {
//密码错误
throw new IncorrectCredentialsException();
}
return new SimpleAuthenticationInfo(username, password, getName());
}
}
我让用户具备role1、role2角色,同时还为之添加了两个普通的权限、user1:*
和user2:*
,这个时候我要稍微修改一个我的ini文件,如下:
[main]
authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
permissionResolver= org.sang.BitAndWildPermissionResolver
authorizer.permissionResolver=$permissionResolver
rolePermissionResolver= org.sang.MyRolePermissionResolver
authorizer.rolePermissionResolver=$rolePermissionResolver
securityManager.authorizer=$authorizer
realm= org.sang.MyRealm
securityManager.realms=$realm
新的测试代码如下:
@Test
public void test99() {
login("classpath:shiro-custom.ini", "wang", "123");
Subject subject = SecurityUtils.getSubject();
Assert.assertTrue(subject.isPermitted("user1:update"));//OK
Assert.assertTrue(subject.isPermitted("user2:update"));//OK
Assert.assertTrue(subject.isPermitted("$user1$2"));//OK
Assert.assertTrue(subject.isPermitted("$user1$8"));//OK
Assert.assertTrue(subject.isPermitted("$user2$10"));//OK
Assert.assertTrue(subject.isPermitted("$user1$4"));//OK
Assert.assertTrue(subject.isPermitted("menu:view"));//OK
}
测试结果如下:
OK,以上就是Shiro中自定义授权的问题。
本文案例 下载: https://github.com/lenve/Shiro/tree/master/Shiro2
更多JavaWeb资料,请移步这里: https://github.com/lenve/JavaEETest
参考资料: 张开涛大神的《跟我学Shiro》,原文连接http://jinnianshilongnian.iteye.com/blog/2018398
以上。
- HDU 2639 Bone Collector II(01背包变形【第K大最优解】)
- 专知内容生产基石-数据爬取采集利器WebCollector 介绍
- python实现字符串模糊匹配
- 动态规划之01背包详解【解题报告】
- hihoCoder #1078 : 线段树的区间修改(线段树区间更新板子题)
- HDU 2546 饭卡(01背包裸题)
- 漫谈文件系统
- AI知识搜索利器:基于ElasticSearch构建专知实时高性能搜索系统
- 【深度干货】专知主题链路知识推荐#5-机器学习中似懂非懂的马尔科夫链蒙特卡洛采样(MCMC)入门教程01
- hihoCoder #1043 : 完全背包(板子题)
- 【深度干货】专知主题链路知识推荐#7-机器学习中似懂非懂的马尔科夫链蒙特卡洛采样(MCMC)入门教程02
- hihoCoder #1038 : 01背包(板子题)
- 最小二乘法多项式曲线拟合原理与实现
- HDU 1166 敌兵布阵(线段树单点更新,板子题)
- 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 数组属性和方法
- Spring整合WebSocket
- Linux 命令(138)—— nc 命令
- # 全网最细 | 21张图带你领略集合的线程不安全
- grafana踩坑记录
- TensorFlow函数:tf.image.crop_to_bounding_box
- tf.newaxis
- tf.random_normal_initializer:TensorFlow初始化器
- 使括号有效的最少添加
- 设计模式~观察者模式
- 网页无插件视频流媒体播放器EasyPlayerPro-IOS版如何解决有声音无画面的问题?
- (建议收藏)Java基础知识笔记二(详细)
- Android 手机如何拍摄RAW图
- 「干货」基本数据类型和引用数据类型的区别
- int 和 integer :装箱和拆箱的过程,会用到什么方法,你觉得这个会对性能有影响吗,原因是什么(百度一面)
- 数组:这个循环可以转懵很多人!