上篇博客(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接口:

  1. public class BitPermission implements Permission {
  2. private String resourceIdentify;
  3. private int permissionBit;
  4. private String instanceId;
  5. public BitPermission(String permissionString) {
  6. String[] array = permissionString.split("$");
  7. if (array.length > 1) {
  8. resourceIdentify = array[1];
  9. }
  10. if (resourceIdentify == null || "".equals(resourceIdentify)) {
  11. resourceIdentify = "*";
  12. }
  13. if (array.length > 2) {
  14. permissionBit = Integer.valueOf(array[2]);
  15. }
  16. if (array.length > 3) {
  17. instanceId = array[3];
  18. }
  19. if (instanceId == null || "".equals(instanceId)) {
  20. instanceId = "*";
  21. }
  22. }
  23. public boolean implies(Permission p) {
  24. if (!(p instanceof BitPermission)) {
  25. return false;
  26. }
  27. BitPermission bitPermission = (BitPermission) p;
  28. if (!("*".equals(resourceIdentify) || this.resourceIdentify.equals(bitPermission.resourceIdentify))) {
  29. return false;
  30. }
  31. if (!(this.permissionBit == 0 || (this.permissionBit & bitPermission.permissionBit) != 0)) {
  32. return false;
  33. }
  34. if (!("*".equals(this.instanceId) || this.instanceId.equals(bitPermission.instanceId))) {
  35. return false;
  36. }
  37. return true;
  38. }
  39. }

OK,这个是我们自定义的权限解析类,该类中有一个implies方法用来执行权限匹配操作,然后我们还需要提供了一个PermissionResolver类,该类将权限字符串解析为相应的类。如下:

  1. public class BitAndWildPermissionResolver implements PermissionResolver {
  2. public Permission resolvePermission(String permissionString) {
  3. if (permissionString.startsWith("$")) {
  4. return new BitPermission(permissionString);
  5. }
  6. return new WildcardPermission(permissionString);
  7. }
  8. }

我们在这里进行简单的判断,如果权限字符串是以$开始的,我们使用自定义的BitPermission进行解析,如果是其他普通的权限字符串,我们就创建WildcardPermission即可。然后我们自定义需要的Realm,在Realm中定义权限,如下:

  1. public class MyRealm extends AuthorizingRealm {
  2. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  3. SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
  4. authorizationInfo.addObjectPermission(new BitPermission("$user1$14"));
  5. authorizationInfo.addStringPermission("$user2$10");
  6. return authorizationInfo;
  7. }
  8. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  9. String username = (String) token.getPrincipal();
  10. String password = new String(((char[]) token.getCredentials()));
  11. if (!"wang".equals(username)) {
  12. //用户名错误
  13. throw new UnknownAccountException();
  14. }
  15. if (!"123".equals(password)) {
  16. //密码错误
  17. throw new IncorrectCredentialsException();
  18. }
  19. return new SimpleAuthenticationInfo(username, password, getName());
  20. }
  21. }

这里我定义了两种权限,一个是$user1$14,表示了create(2),delete(4)和view(8)三种权限;还有一个是$user2$10,表示了create(2)和view(8)两种权限。然后我们来看看在ini文件中如何配置:

  1. [main]
  2. authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
  3. permissionResolver= org.sang.BitAndWildPermissionResolver
  4. authorizer.permissionResolver=$permissionResolver
  5. securityManager.authorizer=$authorizer
  6. realm= org.sang.MyRealm
  7. securityManager.realms=$realm

我们来看看测试代码:

  1. @Test
  2. public void test99() {
  3. login("classpath:shiro-custom.ini", "wang", "123");
  4. Subject subject = SecurityUtils.getSubject();
  5. Assert.assertTrue(subject.isPermitted("$user1$2"));//OK
  6. Assert.assertTrue(subject.isPermitted("$user1$8"));//OK
  7. Assert.assertTrue(subject.isPermitted("$user2$10"));//OK
  8. Assert.assertTrue(subject.isPermitted("$user1$4"));//OK
  9. }

测试结果如下:

绿色表示运行都是OK的。
OK,除了上面 这种自定义的形式之外,我们还可以自定义role解析,根据role的字符串解析出role中的权限集合。

自定义角色解析策略

我们也可以自己来设计角色解析策略,如下:

  1. public class MyRolePermissionResolver implements RolePermissionResolver {
  2. public Collection<Permission> resolvePermissionsInRole(String roleString) {
  3. if ("role1".equals(roleString)) {
  4. return Arrays.asList(((Permission) new WildcardPermission("menu:*")));
  5. }
  6. return null;
  7. }
  8. }

如果用户具有role1角色,那么我让他具有menu:*权限,OK,然后我在Realm中增加两个权限,新的Realm类如下:

  1. public class MyRealm extends AuthorizingRealm {
  2. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  3. SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
  4. authorizationInfo.addRole("role1");
  5. authorizationInfo.addRole("role2");
  6. authorizationInfo.addObjectPermission(new BitPermission("$user1$14"));
  7. authorizationInfo.addObjectPermission(new WildcardPermission("user1:*"));
  8. authorizationInfo.addStringPermission("$user2$10");
  9. authorizationInfo.addStringPermission("user2:*");
  10. return authorizationInfo;
  11. }
  12. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  13. String username = (String) token.getPrincipal();
  14. String password = new String(((char[]) token.getCredentials()));
  15. if (!"wang".equals(username)) {
  16. //用户名错误
  17. throw new UnknownAccountException();
  18. }
  19. if (!"123".equals(password)) {
  20. //密码错误
  21. throw new IncorrectCredentialsException();
  22. }
  23. return new SimpleAuthenticationInfo(username, password, getName());
  24. }
  25. }

我让用户具备role1、role2角色,同时还为之添加了两个普通的权限、user1:*user2:*,这个时候我要稍微修改一个我的ini文件,如下:

  1. [main]
  2. authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
  3. permissionResolver= org.sang.BitAndWildPermissionResolver
  4. authorizer.permissionResolver=$permissionResolver
  5. rolePermissionResolver= org.sang.MyRolePermissionResolver
  6. authorizer.rolePermissionResolver=$rolePermissionResolver
  7. securityManager.authorizer=$authorizer
  8. realm= org.sang.MyRealm
  9. securityManager.realms=$realm

新的测试代码如下:

  1. @Test
  2. public void test99() {
  3. login("classpath:shiro-custom.ini", "wang", "123");
  4. Subject subject = SecurityUtils.getSubject();
  5. Assert.assertTrue(subject.isPermitted("user1:update"));//OK
  6. Assert.assertTrue(subject.isPermitted("user2:update"));//OK
  7. Assert.assertTrue(subject.isPermitted("$user1$2"));//OK
  8. Assert.assertTrue(subject.isPermitted("$user1$8"));//OK
  9. Assert.assertTrue(subject.isPermitted("$user2$10"));//OK
  10. Assert.assertTrue(subject.isPermitted("$user1$4"));//OK
  11. Assert.assertTrue(subject.isPermitted("menu:view"));//OK
  12. }

测试结果如下:

OK,以上就是Shiro中自定义授权的问题。

本文案例 下载:
https://github.com/lenve/Shiro/tree/master/Shiro2

更多JavaWeb资料,请移步这里:
https://github.com/lenve/JavaEETest

参考资料:
张开涛大神的《跟我学Shiro》,原文连接http://jinnianshilongnian.iteye.com/blog/2018398

关注公众号【江南一点雨】,专注于 Spring Boot+微服务以及前后端分离等全栈技术,定期视频教程分享,关注后回复 Java ,领取松哥为你精心准备的 Java 干货!

以上。

Shiro中的授权问题(二)的更多相关文章

  1. Shiro中的授权问题

    在初识Shiro一文中,我们对Shiro的基本使用已经做了简单的介绍,不懂的小伙伴们可以先阅读上文,今天我们就来看看Shiro中的授权问题. Shiro中的授权,大体上可以分为两大类,一类是隐式角色, ...

  2. shiro中的授权

  3. shiro入门学习--授权(Authorization)|筑基初期

    写在前面 经过前面的学习,我们了解了shiro中的认证流程,并且学会了如何通过自定义Realm实现应用程序的用户认证.在这篇文章当中,我们将学习shiro中的授权流程. 授权概述 这里的授权指的是授予 ...

  4. 项目一:第十四天 1.在realm中动态授权 2.Shiro整合ehcache 缓存realm中授权信息 3.动态展示菜单数据 4.Quartz定时任务调度框架—Spring整合javamail发送邮件 5.基于poi实现分区导出

    1 Shiro整合ehCache缓存授权信息 当需要进行权限校验时候:四种方式url拦截.注解.页面标签.代码级别,当需要验证权限会调用realm中的授权方法   Shiro框架内部整合好缓存管理器, ...

  5. 从零到实现Shiro中Authorization和Authentication的缓存

    本文大纲 一.简介 二.缓存的概念 三.自定义实现缓存机制 四.什么是Ehcache 五.Ehcache怎么用 六.Spring对缓存的支持 七.Spring+Ehcache实现 八.Spring+S ...

  6. Apache shiro集群实现 (二) shiro 的INI配置

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

  7. 【shiro】(4)---Shiro认证、授权案例讲解

    Shiro认证.授权案例讲解 一.认证  1. 认证流程     2.用户密码已经加密.加盐的用户认证 (1)测试类 // 用户登陆和退出,这里我自定了一个realm(开发肯定需要自定义realm获取 ...

  8. shiro中基于注解实现的权限认证过程

    授权即访问控制,它将判断用户在应用程序中对资源是否拥有相应的访问权限. 如,判断一个用户有查看页面的权限,编辑数据的权限,拥有某一按钮的权限等等. 一.用户权限模型 为实现一个较为灵活的用户权限数据模 ...

  9. Shiro learning - 入门学习 Shiro中的基础知识(1)

    Shiro入门学习 一 .什么是Shiro? 看一下官网对于 what is Shiro ? 的解释 Apache Shiro (pronounced “shee-roh”, the Japanese ...

随机推荐

  1. Pytorch: cuda runtime error (59) : device-side assert triggered at /pytorch/aten/src/THC/generic/THCTensorMa

    更换了数据集, 在计算交叉熵损失时出现错误 : cuda runtime error (59) : device-side assert triggered at /pytorch/aten/src/ ...

  2. iOS调用系统发送短信和邮件分享

    //发送邮件 -(void)sendMail:(NSString*)subject content:(NSString*)content{ MFMailComposeViewController*co ...

  3. Do Now 一个让你静心学习的APP——团队博客

    Do Now 一个让你静心学习的APP 来自油条只要半根团队的智慧凝聚的产物! 团队博客总目录: 团队作业第一周 团队作业第二周 Do Now -- 团队冲刺博客一 Do-Now-团队Scrum 冲刺 ...

  4. JavaScript遍历对象4种方法和遍历数组的3种方式 代码

    //遍历对象 4种方法 //Object.keys(obj).forEach() console.log("keys...遍历</br>") var obj1 = { ...

  5. 解决audio控制播放音量

    在写手机端项目时,可能会遇到使用audio播放音乐,那么怎样控制audio默认播放的音量呢?下面时解决办法 volume 属于是控制audio 播放音乐的音量,其范围0-1,1表示音量最大 getVi ...

  6. 3.jmeter接口测试---脚本录制

    安装好jmeter后,就要进入主题了,进行接口测试,接口测试的脚本获取方式 ①手动填写 ②badboy录制后,导入jmeter使用 ③jmeter录制 不会安装的可以进入这里:https://www. ...

  7. 从C过渡到C++的几个知识点(结构体、引用、重载运算符)

    一.结构体和类(class) 下面一个使用结构体类型的例子 #include <iostream> using namespace std; struct Point{ // 声明Poin ...

  8. prometheus — 基于文件的服务发现

    基于文件的服务发现方式不需要依赖其他平台与第三方服务,用户只需将要新的target信息以yaml或json文件格式添加到target文件中 ,prometheus会定期从指定文件中读取target信息 ...

  9. MVC是什么

    MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方法组织代码 ...

  10. QEMU KVM Libvirt手册(10): KVM的各种限制

    Overcommits KVM allows for both memory and disk space overcommit. However, hard errors resulting fro ...