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:updateuser:delete等,而user:delete又可以匹配user:delete:1,因此我们可以这样理解:不加*,我们可以做前缀匹配,加*,我们则可以匹配所有。

OK,以上就是Shiro中简单的授权问题。

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

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

原文发布于微信公众号 - 玩转JavaEE(gh_d1ca11234a30)

原文发表时间:2017-03-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java一日一条

理解Java Integer的缓存策略

本文将介绍 Java 中 Integer 缓存的相关知识。这是 Java 5 中引入的一个有助于节省内存、提高性能的特性。首先看一个使用 Integer 的示例...

581
来自专栏Java后端技术栈

Java大型互联网公司经典面试题,论JDK源码的重要性的无限思考

论JDK源码的重要性:一道面试题引发的无限思考!大家在看到这个标题时想的是什么?小编我为什么要讲这个问题呢?

1151
来自专栏HTML5学堂

2015.12.30 HTML5真题练习

HTML5学堂:每天一道题,强壮程序员!今日主要涉及函数与参数的12.29日题目的解答,以及一道简单的作用域的题目。 HTML5真题【2015.12.29】答案...

3306
来自专栏互联网杂技

JS模块与命名空间的介绍

起因 将代码组织到类中的一个重要原因是让代码更加“模块化”,可以在很多不同的场景中实现代码的重用。但类不是唯一的模块化代码的方式。 一般来讲,模块是一个独立的J...

3646
来自专栏专注 Java 基础分享

深入理解Struts2----类型转换

     之前的一系列文章主要介绍了有关Struts2的一些基本用法和部分的简单原理,但是始终没有介绍有关拦截器的相关内容,从本篇开始我们将从另一个角度去深入理...

2239
来自专栏HTML5学堂

2015.12.29 HTML5真题练习

HTML5学堂:每天一道题,强壮程序员!今日主要涉及12.24日题目的解答,以及一道涉及函数作用域的题目。 HTML5真题【2015.12.24】答案解析 12...

2886
来自专栏峰会SaaS大佬云集

C语言中的复制函数(strcpy和memcpy)第三章

1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。

1744
来自专栏数据科学

redis流计算

使用了tornado的异步和streamz的流处理两个库,需要redis 5.0以上版本

1994
来自专栏偏前端工程师的驿站

意译:《JVM Internals》

译者语                                  为加深对JVM的了解和日后查阅时更方便,于是对原文进行翻译。内容是建立在我对JVM的认...

2387
来自专栏HTML5学堂

2015.12.17 HTML5真题练习

HTML5学堂:每天一道题,强壮程序员!今日主要涉及12.16日关于函数返回值的题目解答,以及一道涉及闭包的题目。 HTML5真题【2015.12.16】答案解...

2734

扫码关注云+社区

领取腾讯云代金券