3 Apache Shiro 授权

来源:互联网 发布:c 面向对象编程流程 编辑:程序博客网 时间:2024/06/05 07:52

3.1 概述

授权,又称访问控制,即在应用中控制用户可以使用哪些资源(如访问页面、页面操作、数据操作等),覆盖以下几个概念:
(1) 主体(Subject)
即用户,Shiro 中使用 Subject 代表用户。
(2) 角色(Role)
代表权限的集合,可以将权限集合起来一次性赋予用户。
(3) 权限(Permission)
安全策略中的原子授权单位,权限表示操作某个资源的能力。
(4) 资源(Resource)
应用中可以访问或操作的资源、如页面、数据、业务方法等。

角色又分为:
隐式角色
直接通过角色验证用户是否有某资源的操作权限。如果需要取消该权限,需要将用户对应的角色去除,属于粗粒度的权限控制。
显式角色
通过角色绑定的权限集合验证用户是否有某资源的操作权限。如果需要取消该权限,只需要将该权限从角色绑定的权限集合中去除,不需要去除用户的角色,属于细粒度的权限控制。

3.2 授权方式

Shiro 支持三种授权方式:
(1) 编程式
通过写 if/else 授权代码块实现
(2) 注解式
通过在执行的 Java 方法上放置注解 @RequiresRoles("admin") 实现
(3) JSP/GSP 标签
通过在 JSP/GSP 页面添加标签 <shiro:hasRole> 实现

3.3 授权实现

(1) 基于角色的访问控制(隐式角色)
首先,在 ini 配置文件(shiro-role.ini)中配置用户拥有的角色。

[users]Steve=001,role1,role2Tony=002,role1

在 ini 配置文件中配置用户角色规则:
用户名=密码,角色1,角色2,…
如果需要在应用中判断用户是否具有相应角色,需要在相应的 Realm 中返回角色信息,Shiro 不负责维护用户和角色的信息,需要应用自己提供,Shiro 只提供相应接口实现验证。

其次,编写测试用例。

import static org.junit.Assert.assertFalse;import static org.junit.Assert.assertTrue;import java.util.Arrays;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.config.IniSecurityManagerFactory;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.subject.Subject;import org.apache.shiro.util.Factory;import org.apache.shiro.util.ThreadContext;import org.junit.After;import org.junit.Test;public class ShiroTest {    @Test    public void testHasRole() {        login("classpath:shiro-role.ini", "Steve", "001");        assertTrue(subject().hasRole("role1"));        assertTrue(subject().hasRole("role2"));        boolean[] result = subject().hasRoles(Arrays.asList("role1", "role2", "role3"));        assertTrue(result[0]);        assertTrue(result[1]);        assertFalse(result[2]);    }    private void login(String configFile, String username, String password) {        // 1.获取SecurityManager工厂,使用ini配置文件初始化SecurityManager        Factory<SecurityManager> factory = new IniSecurityManagerFactory(configFile);        // 2.获取SecurityManager实例        SecurityManager securityManager = factory.getInstance();        // 3.将SecurityManager实例绑定给SecurityUtils        SecurityUtils.setSecurityManager(securityManager);        // 4.通过SecurityUtils获取Subject        Subject subject = SecurityUtils.getSubject();        // 5.创建用户名、密码身份验证token        UsernamePasswordToken token = new UsernamePasswordToken(username, password);        // 6.使用用户名、密码身份验证token进行身份认证,即登录        subject.login(token);    }    private Subject subject() {        return SecurityUtils.getSubject();    }    @After    public void tearDown() {        // 如果需要同时运行多个test方法,在每个方法退出时需要解除绑定到当前线程的Subject实例,否则会影响接下来的test方法的执行。        ThreadContext.unbindSubject();    }}

Shiro 提供 boolean hasRole(String roleIdentifier)boolean[] hasRoles(List<String> roleIdentifiers) 方法用于判断用户是否拥有某个角色。
Shiro 还提供了 void checkRole(String roleIdentifier) throws AuthorizationExceptionvoid checkRoles(Collection<String> roleIdentifiers) throws AuthorizationException 方法检查用户是否拥有某个角色,与 hasRolehasRoles 方法的不同之处在于:如果检查出用户不具有某种角色,hasRolehasRoles 方法会返回 false,而 checkRolecheckRoles 方法会抛出 UnauthorizedException 异常。
测试用例(上个测试用例中其它方法不变,只需要将 testHasRole 方法替换为 testCheckRole 方法):

@Test(expected = UnauthorizedException.class)public void testCheckRole() {    login("classpath:shiro-role.ini", "Steve", "001");    subject().checkRole("role1");    subject().checkRoles(Arrays.asList("role1", "role3"));}

(2) 基于资源的访问控制(显式角色)
首先,在 ini 配置文件(shiro-permission.ini)中配置用户拥有的角色及角色、权限关系。

[users]Steve=001,role1,role2Tony=002,role1[roles]#对资源user拥有createupdate权限role1=user:create,user:update#对资源user拥有createdelete权限role2=user:create,user:delete

在 ini 配置文件中配置角色、权限关系的规则:
角色=权限1,权限2,…
配合之前介绍的配置用户角色规则:
用户名=密码,角色1,角色2,…
可知,先根据用户名和密码进行身份认证,通过后根据用户名找到角色,再根据角色找到对应的权限集合。

其次,编写测试用例。

@Testpublic void testIsPermitted() {    login("classpath:shiro-permission.ini", "Steve", "001");    assertTrue(subject().isPermitted("user:create"));    assertTrue(subject().isPermittedAll("user:update", "user:delete"));    assertFalse(subject().isPermitted("user:view"));}

Shiro 提供了 boolean isPermitted(String permission)boolean isPermittedAll(String... permissions) 方法用于判断用户是否拥有某权限。
Shiro 还提供了 void checkPermission(String permission) throws AuthorizationExceptionvoid checkPermissions(String... permissions) throws AuthorizationException 方法检查用户是否拥有某权限,如果检查出用户不拥有目标权限,会抛出 UnauthorizedException 异常。
测试用例:

@Test(expected = UnauthorizedException.class)public void testCheckPermission() {    login("classpath:shiro-permission.ini", "Steve", "001");    subject().checkPermission("user:create");    subject().checkPermissions("user:update", "user:delete");    subject().checkPermission("user:view");}

3.4 字符串通配符权限

规则:资源标识符:操作:对象实例ID,即对哪个资源的哪个实例执行何种操作。其中:

  • “:”用于分隔资源/操作/实例
  • “,”用于分隔多个操作
  • “*”用于表示任意资源、操作或实例

(1) 单个资源的单个权限
java:subject().checkPermissions("system:user:create");
资源 system:usercreate 权限。
(2) 单个资源的多个权限
ini:role41=system:user:update,system:user:delete
java:subject().checkPermissions("system:user:update", "system:user:delete");
资源 system:userupdatedelete 权限。
可以简写为:
ini:role42="system:user:update,delete"
java:subject().checkPermissions("system:user:update,delete");
注意:通过 "system:user:update,delete" 验证 "system:user:update", "system:user:delete" 是没问题的,但是反过来规则不成立。
(3) 单个资源的全部权限

(4) 所有资源的全部权限

(5) 实例级别的权限