shiro学习笔记(3) -- 授权

3. 授权

1. shiro权限初始

​ Shiro 支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即按钮级别的)。

授权过程主要了解几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。

Subject:主体,即访问应用的用户,Subject接口提供了很多方法,主要包括进行认证的登录登出方法、进行授权判断的方法和Session访问的方法。

Resource: 用户想访问的东西。

Permission:安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,例如,一个用户操作实体的CRUD权力。

Role:角色可以理解为自定义权限的集合,达到不用用户的权限粗颗粒实现。举个例子,学生和教师就两个不同的角色,他们分别由读书,做作业,教书,改作业的权限。

2. shiro授权方式

​ Shiro 支持三种方式的授权:

2.1 编程的方式:

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

2.2 注解的方式

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

2.3 注入到JSP中

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

3. 权限判断

3.1 粗颗粒(判断角色)

  1. 编写ini文件(shiro-role.ini)

    [users] 
    zhang=123,role1,role2 #role1,role2 表示角色
    wang=123,role1
    
  2. 编写base 基类,方便后面编写

    public class BaseTest
    {
        /**
         * 模拟登录
         * @param configFile 文件位置
         * @param username  用户名
         * @param password  密码
         */ 
        protected void login(String configFile, String username, String password) {
            IniSecurityManagerFactory factory = new IniSecurityManagerFactory(configFile);
            SecurityManager manager = factory.getInstance();
            SecurityUtils.setSecurityManager(manager);
    
            Subject subject = subject();
    
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            subject.login(token);
        }
        
    	/**
         * 获取Subject
         * @return
         */
        public Subject subject() {
            return SecurityUtils.getSubject();
        }
    }
    
    
  3. 判断角色:hasRole,hasAllRole

    @Test
    public void testHasRole(){
        login("classpath:shiro-role.ini", "zhang", "123");
        //判断拥有角色:role1
        Assert.assertTrue(subject().hasRole("role1"));
    
        Assert.assertTrue(subject().hasAllRoles(Arrays.asList("role1", "role2")));
    
        boolean[] result = subject().hasRoles(Arrays.asList("role1", "role2", "role3"));
        for(int i = 0; i < result.length; i++){
            System.out.println(result[i]);
        }
    }
    
  4. 断言拥有角色,如果未拥有角色,将报UnauthorizedException异常

    @Test(expected = UnauthorizedException.class)
    public void testCheckRole() {
        login("classpath:shiro-role.ini", "zhang", "123");
        //断言拥有角色:role1
        subject().checkRole("role1");
        //断言拥有角色:role1 and role3 失败抛出异常
        subject().checkRoles("role1", "role3");
    }
    

3.2 细颗粒(判断权限)

  1. 编写ini文件(shiro-permission.ini)

    [users]
    zhang=123,role1,role2
    wang=123,role1
    [roles]
    role1=user:create,user:update
    role2=user:create,user:delete
    
  2. 判断是否有权限,isPermittedisPermittedAll

    public class PermissionTest extends BaseTest
    {
        @Test
        public void testPermission(){
            login("classpath:shiro-permission.ini", "zhang", "123");
            //判断拥有权限:user:create
            Assert.assertTrue(subject().isPermitted("user:create"));
            //判断拥有权限:user:update and user:delete
            Assert.assertTrue(subject().isPermittedAll("user:update", "user:delete"));
            //判断没有权限:user:view
            Assert.assertFalse(subject().isPermitted("user:view"));
        }
    }
    
  3. 断言权限,如果没有该权限,会抛出异常UnauthorizedException

    @Test(expected = UnauthorizedException.class) 
         public void testCheckPermission () { 
         login("classpath:shiro-permission.ini", "zhang", "123"); 
         //断言拥有权限:user:create 
         subject().checkPermission("user:create"); 
         //断言拥有权限:user:delete and user:update 
         subject().checkPermissions("user:delete", "user:update"); 
         //断言拥有权限:user:view 失败抛出异常
         subject().checkPermissions("user:view"); 
     }
    

4. 字符串通配符权限

4.1 权限配置规则

​ 规则:“资源标识符:操作:对象实例 ID” 即对哪个资源的哪个实例可以进行什么操作。其默认支持通配符权限字符串,“:”表示资源/操作/实例的分割;“,”表示操作的分割;“*”表示任意资源/操作/实例。

  1. 单个资源配置

    # 用户拥有资源“system:user”的“update”权限。
    role41=system:user:update
    
  2. 单个字段多个权限

    role42=system:user:update,system:user:delete
    

    代码判断

    // 这种方式只能用一下方式来判断
    subject().checkPermissions("system:user:update", "system:user:delete");
    
    // 以这种方式来写就会识别不到
    subject().checkPermissions("system:user:update,delete");
    

    第二种写法:

    role43="system:user:update,delete"
    

    代码判断

    // 以下两种写法都能识别
    subject().checkPermissions("system:user:update", "system:user:delete");
    
    subject().checkPermissions("system:user:update,delete");
    
  3. 单个资源全部权限

    role51="system:user:create,update,delete,view"
    

    代码判断

    // 以下两种写法都能识别
    subject().checkPermissions("system:user:create,delete,update:view");
    
    subject().checkPermissions("system:user:*");
    
  4. 所以资源全部权限

    role61=*:view
    

    代码判断

    subject().checkPermissions("user:view");
    

    用户拥有所有资源的“view”所有权限。假设判断的权限是“"system:user:view”,那么需要“role5=::view”这样写才行。

5. 授权过程

流程如下:

  1. 首先调用Subject.isPermitted*/hasRole*接口,其会委托给SecurityManager,而SecurityManager接着会委托给Authorizer;
  2. Authorizer是真正的授权者,如果我们调用如isPermitted(“user:view”),其首先会通过PermissionResolver把字符串转换成相应的Permission实例;
  3. 在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限;
  4. Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted/hasRole会返回true,否则返回false表示授权失败。

ModularRealmAuthorizer进行多Realm匹配流程:

  1. 首先检查相应的Realm是否实现了实现了Authorizer;
  2. 如果实现了Authorizer,那么接着调用其相应的isPermitted*/hasRole*接口进行匹配;
  3. 如果有一个Realm匹配那么将返回true,否则返回false。

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

1.1 如果调用hasRole,则直接获取AuthorizationInfo.getRoles()与传入的角色比较即可;

1.2 首先如果调用如isPermitted(“user:view”),首先通过PermissionResolver将权限字符串转换成相应的Permission实例,默认使用WildcardPermissionResolver,即转换为通配符的WildcardPermission;

2 通过AuthorizationInfo.getObjectPermissions()得到Permission实例集合;通过AuthorizationInfo. getStringPermissions()得到字符串集合并通过PermissionResolver解析为Permission实例;然后获取用户的角色,并通过RolePermissionResolver解析角色对应的权限集合(默认没有实现,可以自己提供);

3 接着调用Permission. implies(Permission p)逐个与传入的权限比较,如果有匹配的则返回true,否则false。

6. Authorizer、PermissionResolver及RolePermissionResolver

​ Authorizer的职责是进行授权(访问控制),是Shiro API中授权核心的入口点,其提供了相应的角色/权限判断接口,具体请参考其Javadoc。SecurityManager继承了Authorizer接口,且提供了ModularRealmAuthorizer用于多Realm时的授权匹配。

​ PermissionResolver用于解析权限字符串到Permission实例,而RolePermissionResolver用于根据角色解析相应的权限集合。

代码太多了,我不想写了,我是参考这个文章来学习的

第三章 授权——《跟我学Shiro》

他这篇文章有一个问题,就是在定义shiro-authorizer.ini文件时,少写几行代码

#没有调用自己声明的Realm
#自定义realm 一定要放在securityManager.authorizer赋值之后(因为调用setRealms会将realms设置给authorizer,并给各个Realm设置permissionResolver和rolePermissionResolver)
fooRealm=realm.MyRealm
securityManager.realms=$fooRealm

然后就是他没有提供shiro-jdbc-authorizer.ini

[main]
#自定义authorizer
authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
#自定义permissionResolver
#permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolver
permissionResolver=permission.BitAndWildPermissionResolver
authorizer.permissionResolver=$permissionResolver
#自定义rolePermissionResolver
rolePermissionResolver=permission.MyRolePermissionResolver
authorizer.rolePermissionResolver=$rolePermissionResolver

securityManager.authorizer=$authorizer

#自定义realm 一定要放在securityManager.authorizer赋值之后(因为调用setRealms会将realms设置给authorizer,并给各个Realm设置permissionResolver和rolePermissionResolver)
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
dataSource.password=root
jdbcRealm.dataSource=$dataSource
jdbcRealm.permissionsLookupEnabled=true

securityManager.realms=$jdbcRealm

其他的你跟着上面的文章就可以了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值