Shiro 授权流程详解

1. 授权

  授权即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。

2. 关键对象

  授权可简单理解为 who 对 what(which)进行 How 操作;

  Who,即主体(Subject),主体需要访问系统中的资源;

  What,即资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型和资源实例,比如商品信息为资源类型,类型为 t01 的商品为资源实例,编号 001 的商品也属于资源实例;

  How,权限 / 许可(Permission),规定了主体对资源的操作许可, 权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为 001 用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。

3. 授权流程

     

4. 授权方式

  • 基于角色的访问控制【RBAC 基于角色的访问控制(Role-Based Access Control)是以角色为中心进行访问控制】
if(subject.hasRole("admin")){
   //操作什么资源
}
  • 基于资源的访问控制【RBAC 基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制】
if(subject.isPermission("user:update:01")){ //资源实例
  //对01用户进行修改
}
if(subject.isPermission("user:update:*")){  //资源类型
  //对01用户进行修改
}

5. 权限字符串

  权限字符串的规则是【资源标识符:操作:资源实例标识符】意思是对哪个资源的哪个实例具有什么操作,“:”是资源/操作/实例的分割符,权限字符串也可以使用 * 通配符。

例子:

  • 用户创建权限:user : create,或 user : create : *

  • 用户修改实例001的权限:user : update : 001

  • 用户实例001的所有权限:user : *:001

6. 开发授权代码

  a 自定义 Realm 代码实现

public class CustomerMD5Realm extends AuthorizingRealm {

    /**
     * 一个主题可以有多个身份,参数是集合 principals;但是只有一个主身份;
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 获取身份信息
        String principal = (String) principals.getPrimaryPrincipal();
        System.out.println("身份信息 = " + principal);

        // 根据身份信息 用户名 获取当前用户的角色信息,以及权限信息【比如:从数据库中获取】
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        // 将数据库中查询角色信息赋值给权限对象
        simpleAuthorizationInfo.addRoles(Arrays.asList("admin", "user"));
        simpleAuthorizationInfo.addStringPermission("user:*:01");
        simpleAuthorizationInfo.addStringPermission("product:*:*");
        return simpleAuthorizationInfo;
    }

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String principal = (String) token.getPrincipal();
        System.out.println("principal = " + principal);
        return null;
    }
}

  b 授权处理【会自动调用自定义 CustomerMD5Realm 的 doGetAuthorizationInfo 获取指定用户的权限信息】

public class TestCustomerMD5Authenticator {

    public static void main(String[] args) {
        // 1. 创建安全管理器
        DefaultSecurityManager securityManager = new DefaultSecurityManager();

        // 2. 给安全管理器设置 realm 【Realm 需要获取用户认证的数据源信息】
        CustomerMD5Realm customerMD5Realm = new CustomerMD5Realm();
        // 设置 realm 使用的 hash 凭证匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("md5"); // 设置使用的 Hash 算法
        credentialsMatcher.setHashIterations(1024);     // 需要散列多少次
        customerMD5Realm.setCredentialsMatcher(credentialsMatcher);
        securityManager.setRealm(customerMD5Realm);

        // 3. SecurityUtils 设置安全管理器
        SecurityUtils.setSecurityManager(securityManager);

        // 4. 获取关键对象 Subject 主体
        Subject subject = SecurityUtils.getSubject();

        // 5. 创建令牌
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");

        // 用户认证
        try {
            System.out.println("认证状态: " + subject.isAuthenticated());
            subject.login(token);
            System.out.println("认证状态: " + subject.isAuthenticated());
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("认证失败: 用户名不存在~");
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            System.out.println("认证失败: 密码错误~");
        }

        // 授权
        if (subject.isAuthenticated()) {
            //基于角色权限控制
            System.out.println(subject.hasRole("super"));  // 走授权流程

            // 基于多角色权限控制
            System.out.println(subject.hasAllRoles(Arrays.asList("admin", "user")));  // 走授权流程

            // 是否具有其中一个角色
            boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user", "super"));  // 走授权流程
            for (boolean aBoolean : booleans) {
                System.out.println(aBoolean);
            }

            // 基于权限字符串的访问控制 资源标识符:操作:资源类型
            System.out.println(subject.isPermitted("user:update:01"));  // 走授权流程

            // 分别具有哪些权限
            boolean[] permitted = subject.isPermitted("user:*:01", "order:*:02");  // 走授权流程
            for (boolean b : permitted) {
                System.out.println(b);
            }
        }
    }

}

  注意:深入了解 Shiro 的授权流程,可以 debug 运行程序,查看源代码进行学习深究!

 

posted @ 2021-05-28 17:04  菜鸟的奋斗之路  阅读(799)  评论(0编辑  收藏  举报