Shiro实现授权
0 Views with
本文字数:1,989 字 | 阅读时长 ≈ 8 min

Shiro实现授权

0 Views with
本文字数:1,989 字 | 阅读时长 ≈ 8 min

Shiro实现授权

授权,也叫做访问控制,即在应用中控制谁能访问哪些资源(如访问页面、编辑数据、页面操作等)。在授权中需要了解几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。

授权的概念

主体:
主体,即访问应用的用户,在Shiro中使用Subject代表用户。用户只有授权后才允许访问相应的资源。

资源:
在应用中用户可以访问的任何东西都称为资源。用户只有授权后才能访问。

权限:
安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权利。即权限表示在应用中用户能不能访问某个资源。
shiro支持粗颗粒度权限(如用户模块的所有权限)和细颗粒度权限(操作某个用户的权限,即实例级别的)。

角色:
角色代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限。不同的角色拥有一组不同的权限。

隐式角色:
即直接通过角色来验证用户有没有操作权限。比如:在应用中班长和课代表可以使用打印机,但是某一天老师不允许课代表使用打印机了,我们就需要将应用中课代表操作打印机的权限代码删除。即粒度是以角色为单位进行访问控制的,粒度较粗。

显示角色:
在程序中通过权限控制谁能访问某个资源,角色聚合一组权限集合;这样假设某个角色不能访问某个资源,只需要从角色代表的权限集合中移除即可;无需修改多处代码;即粒度是以资源、实例为单位的,粒度较细。

授权流程

解释:

ModularRealmAuthorizer进行多Realm匹配流程:

如果Realm进行首选的话,应该继承AuthorizingRealm,其流程是:

授权方式

Shiro支持三种方式的授权:

编程式: 通过写if/else授权代码块完成:

Subject subject = SecurityUtils.getSubject();
if(subject.hasRole("admin")){
    //有权限
} else{
    //无权限
}

注解式: 通过在指定的Java方法上放置相应的注解完成:

@RequiresRoles("admin")
public void hello(){
    //有权限
}

没有权限将抛出相应的异常。

JSP标签: 在JSP页面通过相应的标签完成:

<shiro:hasRole name="admin">
    <!-- 有权限 -->
</shiro:hasRole>

实现授权

基于角色

基于角色的访问控制(隐式角色)

1、shiro-role.ini

[users]
tycoding=123,role1,role2

补充
此处ini配置文件的规则:用户名=密码,角色1,角色2,...。如果在需要在应用中判断用户是否拥有相应角色,就需要需要在相应的Realm中返回角色信息,也就是说Shiro不负责维护用户-角色信息,Shiro只是提供了相应的接口方便验证。

2、RoleTest.java

@Test
public void testHasRole() {

    //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
    Factory factory = new IniSecurityManagerFactory("classpath:shiro-role.ini");

    //2、得到SecurityManager实例,并绑定给SecurityUtils
    SecurityManager securityManager = (SecurityManager) factory.getInstance();
    SecurityUtils.setSecurityManager(securityManager);

    //3、得到Subject及创建用户名、密码身份的Token
    Subject subject = SecurityUtils.getSubject();
    UsernamePasswordToken token = new UsernamePasswordToken("tycoding", "123");

    try {
        //4、登录,即身份验证
        subject.login(token);

        //判断用户是否拥有角色:role1
        System.out.println(subject.hasRole("role1"));

        //判断用户是否拥有角色:role1、role2
        boolean[] check1 = subject.hasRoles(Arrays.asList("role1", "role2"));
        for (boolean b: check1) {
            System.out.println(b);
        }

        //判断用户是否拥有角色:role1、role2、role3
        boolean[] check2 = subject.hasRoles(Arrays.asList("role1", "role2", "role3"));
        for (boolean b: check2) {
            System.out.println(b);
        }

    } catch (AuthenticationException e) {
        //5、身份验证失败
        e.printStackTrace();
    }

    //6、退出
    subject.logout();
}

3、打印结果

true
true

true
true
false

4、总结
如上就是基于角色的访问控制(即隐式角色),这种方式的缺点如果很多地方都进行了角色的判断,但是某一天不需要了,就要把相关的代码删除掉;这就是粗颗粒度造成的问题。

基于资源

基于资源的访问控制(显示角色)

1、shiro-permission.ini

[users]
tycoding=123,role1,role2

[roles]
role1:user:create,user:update
role2:user:create,user:delete

补充
此处ini配置文件的规则:”用户名=密码,角色1,角色2” “角色=权限1,权限2”。即首先根据用户名找到角色,然后再根据角色找到权限;即角色是权限的集合;Shiro同样不进行权限的维护,需要我们通过Realm返回相应的权限信息。只需要维护”用户-角色”之间的关系即可。

2、RoleTest.java

@Test
public void testPermissionRole() {
    //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
    Factory factory = new IniSecurityManagerFactory("classpath:shiro-permission.ini");

    //2、得到SecurityManager实例,并绑定给SecurityUtils
    SecurityManager securityManager = (SecurityManager) factory.getInstance();
    SecurityUtils.setSecurityManager(securityManager);

    //3、得到Subject及创建用户名、密码身份的Token
    Subject subject = SecurityUtils.getSubject();
    UsernamePasswordToken token = new UsernamePasswordToken("tycoding", "123");

    try {
        //4、登录,即身份验证
        subject.login(token);

        //判断用户是否拥有权限:user:create
        System.out.println(subject.isPermitted("user:create"));

        //潘墩用户是否拥有权限:user:update和user:delete
        boolean[] check = subject.isPermitted("user:create", "user:delete");
        for (boolean b: check) {
            System.out.println(b);
        }

    } catch (AuthenticationException e) {
        //5、身份验证失败
        e.printStackTrace();
    }

    //6、退出
    subject.logout();
}

3、打印结果:

true

true
true

4、总结
如上,我们事先了基于资源的访问控制(显示角色)。这种方式的优势显而易见,主要体现角色是权限的集合,这种方式的规则主要是资源标识符:操作,即是资源级别的粒度。如果我们需要更改某个角色的权限,只需要一个资源级别的修改,不会对其他模块代码产生影响,粒度小。需要维护用户--角色,角色--权限之间的关系。


交流

如果大家有兴趣,欢迎大家加入我的Java交流群:671017003 ,一起交流学习Java技术。博主目前一直在自学JAVA中,技术有限,如果可以,会尽力给大家提供一些帮助,或是一些学习方法,当然群里的大佬都会积极给新手答疑的。所以,别犹豫,快来加入我们吧!


联系

If you have some questions after you see this article, you can contact me or you can find some info by clicking these links.

如果你觉得这篇文章帮助到了你,你可以帮作者买一杯果汁表示鼓励

Jul 09, 2018