shiro----shiro简单使用与源码分析(二)授权

2019-04-07  本文已影响0人  不过意局bugyj

上接一
隐式显式角色部分参考并截取了部分大佬


文章

授权

相关概念:

显式角色的有点就是在代码中只需要判断是否有某一种权限即可,对某资源有某种操作这种东西是固定存在的,而角色可能有删除,有添加,每次删除或添加都得动源代码来增加或删除判断语句,后期维护需要很大的成本。做法就是这个博客中提到的:

隐式角色权限判断判断
显示角色权限判断

官方文档中建议:使用isPermitted或checkPermitted方法而不是使用hasRoles或checkRoles

often a better way of performing access control is through permission-based authorization.

和用户名密码一样,角色信息也来自于数据库或缓存中。然后AuthorizationInfo.addRole()加入到用户中。

检测权限

三种方式:

Subject currentUser = SecurityUtils.getSubject();

Permission p = new WildcardPermission("printer:print:laserjet4400n");

if (currentUser.isPermitted(p) {
    //show the Print button
} else {
    //don't show the button?  Grey it out?
}
Subject currentUser = SecurityUtils.getSubject();

//guarantee that the current user is permitted
//to open a bank account:
Permission p = new AccountPermission("open");
currentUser.checkPermission(p);
openBankAccount();
//or
Subject currentUser = SecurityUtils.getSubject();

//guarantee that the current user is permitted
//to open a bank account:
currentUser.checkPermission("account:open");
openBankAccount();

注意:注解方式不能和@Transaction重复使用,因为两个注解都是通过动态代理模式实现的,不能叠加动态代理。
示例代码:

 @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("do authorize");

//        1. 从principle中获取用户信息
        Object principle = principalCollection.getPrimaryPrincipal();
//        2. 利用登录用户信息老获取当前用户的角色和权限
        Set<String> roles = new HashSet<>();
        if ("root".equals(principle)) {
            roles.add("root");

        } else if ("admin".equals(principle)) {
            roles.add("admin");
        }
//        3. 构造AuthenticationInfo并设置其roles属性
        AuthorizationInfo info = new SimpleAuthorizationInfo(roles);
//        4. 返回
        return info;
    }

然后再配置两个controller方法:

    @RequestMapping(value = "/user/root")
    @RequiresRoles("root")
    @ResponseBody
    public String getRootPage() {
        sessionService.testSession();
        return "<h1>only root page</h1>";
    }

    @RequestMapping(value = "/user/admin")
    @RequiresRoles("admin")
    @ResponseBody
    public String getAdminPage() {
        sessionService.testSession();
        return "<h1>only admin page</h1>";
    }

最后为两个方法url设置拦截器:

/user/root=roles[root]
/user/admin=roles[admin]

可见在示例代码中root用户的角色名字因为root,admin的角色名也为admin,试验效果即root用户登录能访问/user/root,而不能访问/user/admin,会自动跳转到applicationContext文件中配置好的unauthorizedUrl页面显示权限不足。

流程分析

暂且使用这种权限验证方法测试下运行流程,:

  1. 在相应代码段打上断点,访问设置了权限拦截器的页面。
  2. 首先停留在了RolesAuthorizationFilter处,这就是我们前面在配置文件中配置的roles拦截器,它调用subject的hasAllRoles方法验证用户是否有相应角色。
  3. 进入subject的子类DelegatingSubject类中,可以看到subject如出一辙的使用了SecurityManager验证授权。
  4. 进入到SecurityManager的子类AuthorizingSecurityManager方法。调用自身属性authorizer验证。
  5. 之后也就和认证一样进入上一条说的authorizer对象的子类ModularRealmAuthorizer的hasAllRolers方法中:然后调用自身hasRole方法:

    到达我们熟悉的realm对象:
  6. 下一步还是先进入我们实现的realm的父类AuthorizingRealm的getAuthorizationInfo方法中,然后调用自身的模板方法doGetAuthorizationInfo,这才来到我们实现的MyRealm类中。



    获取权限信息,一层层返回给RolesAuthorizationFilter中验证。

同理:使用checkPermitted时就是以Subject的checkPermitted方法为入口,其他大致差不多。

修改成permission-based方式:

将配置修改

/user/root=perms[root:view]
/user/admin=perms[admin:view]

然后注解删除

//    @RequiresRoles("root")
//    @RequiresRoles("admin")

在MyRealm方法中设置表示权限的字符串(存储在数据库中):

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        if ("root".equals(principle)) {
            info.addStringPermission("root:*:*");
        } else if ("admin".equals(principle)) {
            info.addStringPermission("admin:*:*");
        }
        return info;

也可以封装权限字符串:

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        if ("root".equals(principle)) {
            Permission permission = new WildcardPermission("root:*:*")
            info.addObjectPermission(permission);
        } else if ("admin".equals(principle)) {
            Permission permission = new WildcardPermission("admin:*:*")
            info.addObjectPermission(permission);
        }
//        4. 返回
        return info;

然后访问即可



root身份登录,点击adminpege跳转到error.html文件中。



访问rootpage:

加密

上一篇 下一篇

猜你喜欢

热点阅读