• 第三章 授权(学习笔记)


    授权需要了解到的几个对象:主体(Subject),资源(Resource),权限(Permission),角色(Role)

    主体:即访问的用户

    资源:如 html jsp 页面,查看编辑等操作

    权限:表示在应用中用户能否访问某个权限

    角色:权限的集合

    隐式角色:通过角色验证用户有没有操作权限,

    显示角色:在程序中通过权限控制谁能访问某个资源,比如说不让某角色访问某个权限,直接在角色代表的权限集合中删掉即可

    3.1 授权

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

    1、在ini配置文件配置用户拥有的角色(shiro-role.ini) 

    [users]
    zhang=123,role1,role2
    wang=123,role1

    规则:用户名=密码,角色1,角色2

    public void login(String ini,String username,String password){
            //1.创建    SecurityManager 工厂
            Factory<SecurityManager> factory = new IniSecurityManagerFactory(ini);
            //2.得到SecurityManager实例
            SecurityManager securityManager = factory.getInstance();
            SecurityUtils.setSecurityManager(securityManager);
            //3.得到Subject 创建用户名密码  身份验证Token
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(username,password);
            
            //4.身份验证
            subject.login(token);
            
            //用户是否登录
            Assert.assertEquals(true, subject.isAuthenticated());
            //5.退出
            //subject.logout();
        }
       @Test
        public void testRole(){
            login("classpath:shiro-role.ini","zhang","123");
            
            Subject subject = SecurityUtils.getSubject();
            //判断拥有角色
            Assert.assertEquals(true,subject.hasRole("role1"));
            //判断拥有角色
            Assert.assertEquals(true,subject.hasAllRoles(Arrays.asList("role1","role2")));
            //判断有哪些角色
            boolean [] result = subject.hasRoles(Arrays.asList("role1","role2","role3"));
            System.out.println(result[0]);
            System.out.println(result[1]);
            System.out.println(result[2]);
            
            //checkRole/checkRoles判断为假的情况下会抛出UnauthorizedException异常。
            subject.checkRole("role1");  
            //断言拥有角色:role1 and role3 失败抛出异常  
            subject.checkRoles("role1", "role3");  
        }
        

    checkRole/checkRoles 如果验证失败会抛出 UnauthorizedException异常

    到此基于角色的访问控制(即隐式角色)就完成了,这种方式的缺点就是如果很多地方进行了角色判断,但是有一天不需要了那么就需要修改相应代码把所有相关的地方进行删除;这就是粗粒度造成的问题。

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

    1、在ini配置文件配置用户拥有的角色及角色-权限关系(shiro-permission.ini) 

    [users]  
    zhang=123,role1,role2  
    wang=123,role1  
    [roles]  
    role1=user:create,user:update  
    role2=user:create,user:delete  

    规则:角色=权限1,权限2

    首先通过用户找角色,角色是一个权限的集合 ,我们直接维护用户和角色之间的关系即可

     Assert.assertTrue(subject.isPermitted("user:create"));
     Assert.assertTrue(subject.isPermittedAll("user:create","user:delete"));
             
     //没有权限会抛出UnauthorizedException异常
     subject.checkPermission("user:create");
     subject.checkPermissions("user:create","user:delete"); 

    3.2 Permission

    字符串通配符权限

    规则:资源标识符:操作:对象实例ID

    如:user:update:1    代表可以修改ID为1的用户

    role3=user:update,user:delete  

    多个权限的也可以这么写:

    role31="user:update,delete"
    subject.checkPermissions("user:update,delete"); 

    单个资源所有权限

    role4=system:user:*  

    *可以不加 如:role4=system:user  

    这两种都可以

    subject.checkPermissions("system:user:update,delete,save"); 
    subject.checkPermissions("system:user:*"); 

    所有资源全部权限

    role5=*:view

    通过代码判断

    subject.checkPermissions("user:view"); 

    假设我们要判断:system:user:view    需要将role5=*:view 改为 *:*:view 这样才行

    system:user:* 等价于 system:user  ,  *:*:view 不等于 user:view 

    3.3 授权流程

    我这里简单的画了下授权的流程

    1.首先调用Subject.isPermitted*/hasRole* 接口,他会委托给securityManager ,接着在委托给Authorizer

     securityManager.checkPermissions(getPrincipals(), permissions);

    2.ModularRealmAuthorizer  循环Realm  

    3.realm.isPermitted  方法中 通过  PermissionResolver 获取 Permission 实例

    4.WildcardPermission 中匹配字符串 在这之前呢会通过getAuthorizationInfo(principals)获取realm 中配置

    5.逐个进行匹配,匹配成功返回true,否则返回false  

    说白了就是把你传进的字符串 user:view  分割开 放入List<Set>  然后在获取realm中用户所对应的权限分隔开逐个对比

    3.4 Authorizer、PermissionResolver及RolePermissionResolver

    Authorizer 负责授权(访问控制) 

    PermissionResolver 用于解析字符串得到Permission实例 

    RolePermissionResolver 用于根据角色解析相应的权限集合

    自定义授权

    1、ini配置(shiro-authorizer.ini    

    authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
    
    #自定义字符串匹配类 permissionResolver
    =com.zls.shiro.BitAndWildPermissionResolver authorizer.permissionResolver=$permissionResolver #根据角色获取权限 rolePermissionResolver=com.zls.shiro.MyRolePermissionResolver authorizer.rolePermissionResolver=$rolePermissionResolver securityManager.authorizer=$authorizer #自定义realm 一定要放在securityManager.authorizer赋值之后 #(因为调用setRealms会将realms设置给authorizer,并给各个Realm设置permissionResolver和rolePermissionResolver) realm=com.zls.shiro.realm.MyRealm securityManager.realms=$realm

    2、定义BitAndWildPermissionResolver及BitPermission

    BitPermission用于实现位移方式的权限,如规则是:

    权限字符串格式:+资源字符串+权限位+实例ID;以+开头中间通过+分割;

    权限:0 表示所有权限;1 新增(二进制:0001)、2 修改(二进制:0010)、4 删除(二进制:0100)、8 查看(二进制:1000);

    如 +user+10  表示对资源user拥有修改/查看权限。 10的二进制是 1010  

    这个方法与WildcardPermission作用是一样的 

    
    

    public class BitPermission implements Permission{

    private String resourceIdentify; //资源
        String instanceId;  //实例
        private int permissionBit; //权限
        
        public BitPermission(String permissionString){
            String [] array = permissionString.split("\+");
            if(array.length > 1){
                resourceIdentify = array[1];
            }
            
            if(StringUtils.isEmpty(resourceIdentify)){
                resourceIdentify = "*";
            }
            
            if(array.length > 2){
                permissionBit = Integer.valueOf(array[2]);
            }
            
            if(array.length > 3){
                instanceId = array[3];
            }
            
            if(StringUtils.isEmpty(instanceId)){
                instanceId = "*";
            }
        }
        
        @Override
        public boolean implies(Permission p) {
            if(!(p instanceof BitPermission)){
                return false;
            }
            
            BitPermission other = (BitPermission) p;
            if(!("*".equals(this.resourceIdentify) || this.resourceIdentify.equals(other.resourceIdentify))){
                return false;
            }
            
            if(!(this.permissionBit == 0 || (this.permissionBit & other.permissionBit) != 0)){
                return false;
            }
            
            if(!("*".equals(this.instanceId) || this.instanceId.equals(other.instanceId))){
                return false;
            }
            
            
            return true;
        }
    }

    用+号区分自定义授权

    public class BitAndWildPermissionResolver implements PermissionResolver {  
        @Override  
        public Permission resolvePermission(String permissionString) {  
            if(permissionString.startsWith("+")) {  
                return new BitPermission(permissionString);  
            }  
            return new WildcardPermission(permissionString);  
        }  
    }   

    3、定义MyRolePermissionResolver

    public class MyRolePermissionResolver implements RolePermissionResolver {  
        @Override  
        public Collection<Permission> resolvePermissionsInRole(String roleString) {  
            if("role1".equals(roleString)) {  
                return Arrays.asList((Permission)new WildcardPermission("menu:*"));  
            }  
            return null;  
        }  
    }   

    如果用户拥有role1,那么就返回一个“menu:*”的权限。

    4、自定义Realm

    public class MyRealm extends AuthorizingRealm{
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(
                PrincipalCollection principals) {
            
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            authorizationInfo.addRole("role1");
            authorizationInfo.addRole("role2");
            authorizationInfo.addObjectPermission(new BitPermission("+user+10"));
            authorizationInfo.addObjectPermission(new WildcardPermission("user1:*"));
            authorizationInfo.addStringPermission("+user2+10");
            authorizationInfo.addStringPermission("user2:*");
            
            return authorizationInfo;
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken token) throws AuthenticationException {
            UsernamePasswordToken upToken  = (UsernamePasswordToken) token;
            //获取用户名
            String username = upToken.getUsername();
            String password = String.valueOf(upToken.getPassword());
            
            if(!"zhang".equals(username)) {  
                throw new UnknownAccountException(); //如果用户名错误  
            }  
            if(!"123".equals(password)) {  
                throw new IncorrectCredentialsException(); //如果密码错误  
            }  
            
            //如果身份认证验证成功,返回一个AuthenticationInfo实现;  
            return new SimpleAuthenticationInfo(username, password, getName());  
        }
    
    }

    此次还要注意就是不能把我们自定义的如“+user1+10”配置到INI配置文件,即使有IniRealm完成,因为IniRealm在new完成后就会解析这些权限字符串

    现在就可以来进行测试了 

      //判断拥有权限:user:create  
       Assert.assertTrue(subject().isPermitted("user1:update"));  
       Assert.assertTrue(subject().isPermitted("user2:update"));  
       //通过二进制位的方式表示权限  
       Assert.assertTrue(subject().isPermitted("+user1+2"));//新增权限  
       Assert.assertTrue(subject().isPermitted("+user1+8"));//查看权限  
       Assert.assertTrue(subject().isPermitted("+user2+10"));//新增及查看  

    有的朋友可能不明白这里 为什么!=0 就返回true

     if(!(this.permissionBit == 0 || (this.permissionBit & other.permissionBit) != 0)){
          return false;
     }

    & : 位运算符,打个比方 2 & 10

    计算这个总共分三步

    1:转二进制  0010   1010

    2:比 

    0010

    1010

    当两位都是1的时候为1,否则就是0

    结果就是 0010

    3:在转二进制 0010  就是 2

    这回大家懂了吧,只要大于0 就说明是有权限的

  • 相关阅读:
    c#+linux+mono+Redis集群(解决无法连接Redis的问题)
    实验楼----奇妙的音乐
    实验楼----PHP大法
    实验楼----PHP代码审计(sha1、md5)
    实验楼----变异凯撒
    storm安装
    storm问题汇总
    windows下linux子系统安装
    mongoDB学习记录
    excel vba 不可查看
  • 原文地址:https://www.cnblogs.com/zls1218/p/8668827.html
Copyright © 2020-2023  润新知