shiro讲解 之 Authorization (三)
shiro讲解 之 Authorization (三)
在之前的章节中我们学习了Shiro 的授权方式和实现。就Shiro 的 授权粒度而言,我们之前学习都是Shiro 的粗粒度。在授权粒度上 Shiro 做的非常好,即我们既可以实现粗粒度的授权(一般指 Authorization)和细粒度的鉴权(Permission)。
概念
一定程度上而言 Shiro 的 Permisssion 指的是Shiro 对权限细粒度的划分。在我们之前学习的章节中给我们所接触到的 Authorization都是粗粒度的授权即一个实例(角色)拥有相应资源的所有操作权限。
Shiro Permission 在做授权是规定某些实例在访问某系特定资源(该实例享有的前提下)时能做哪些操作(对资源而言)。我想这样来解释 Shiro Permission 仍然很抽象。下面我用一个案例来说明一下 Shiro Persimission 定义的体现:
假如公司有一台名为Printer158 的打印机(资源),我们在使用 Shiro 做权限设计时采取一下做法:
- 管理员(角色或者实例)能够对该台打印机做任何操作(比如初始化设置、设置、内部数据管理、打印等)。
- 公司员工(角色或者实例) 只能使用打印功能(操作)。
在以上的案例中有三个很关键的词语:资源-操作-实例。较为完整的表达为某个资源可以被做某些操作于某些实例而言,这样的表达在中文中是非常拗口的但你若了解外国人(尤其是欧美人)的表达方式你就会发现这其实是按照外国人的思维逻辑和表达方式惯性的体现,这里就不赘述了。
资源-操作-实例
资源-操作-实例 是 Shiro 做细粒度鉴权 persmission时的一种规则。
含义
- 对哪个资源的哪个实例可以进行什么操作
扩展
- 默认支持通配符权限字符串,: 表示资源/操作/实例的分割;, 表示操作的分割,* 表示任意资源/操作/实例。
单个权限
- user:query、user:edit。
- 冒号是一个特殊字符,它用来分隔权限字符串的下一部件:第一部分是权限被操作的领域,第二部分是被执行的操作。
- 多个值:每个部件能够保护多个值。因此,除了授予用户 user:query和 user:edit 权限外,也可以简单地授予他们一个:user:query, edit。
- 还可以用 * 号代替所有的值,如:user:* , 也可以写:*:query,表示某个用户在所有的领域都有 query 的权限。
例子
- 单个资源多个权限 user:query user:add 多值 user:query,add
- 单个资源所有权限 user:query,add,update,delete user:*
- 所有资源某个权限 *:view
实例级访问控制
- 规则: 资源标识符:操作:对象实例 ID
- 这种情况通常会使用三个部件:域、操作、被付诸实施的实例。如:user
manager
- 也可以使用通配符来定义,如:user
、user::、user::manager
- 部分省略通配符:缺少的部件意味着用户可以访问所有与之匹配的值,比如:user:edit 等价于 user:edit :*、
user 等价于 user:\ :** - 注意:通配符只能从字符串的结尾处省略部件,也就是说 user:edit 并不等价于 user:*:edit
例子
单个实例的单个权限 printer
lp7200 printer
epsoncolor
- 对资源printer的lp7200实例拥有query权限。
- 对资源printer的epsoncolor实例拥有query权限。
所有实例的单个权限 printer
*
- 对资源printer的所有r实例拥有query权限。
所有实例的所有权限 printer::
- 对资源printer的1实例拥有所有权限。然后通过如下代码判断
subject().checkPermissions(“printer1”, “printer
2”);
- 对资源printer的1实例拥有所有权限。然后通过如下代码判断
- 单个实例的所有权限 printer:*:lp7200
* 对资源printer的lp7200实例拥有所有权限
* 单个实例的多个权限 printer:query,print:lp7200
* 对资源printer的lp7200实例拥有query,print权限
实例
下面我们将通过实例来深了解 Shiro permisssion细粒度鉴权是怎样实现的。
- 延用之前的框架Spring+shiro+Spring MVC
配置filterChainDefinition
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login/toLogin" />
<property name="successUrl" value="/example/index" />
<property name="unauthorizedUrl" value="/example/unauthorized" />
<property name="filterChainDefinitions">
<value>
/login/toLogin = anon
/login/loginVal = anon
/login/logout = logout
/example/admin =roles[admin]
/example/admin =perms[admin
*]
/example/user = roles[user]
/example/user = perms[user
*]
/** = authc
</value>
</property>
</bean>
使用多Realm 做 Authorization
ShiroRealm
// 授权会被 shiro 回调的方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("ShiroRealm Authorization");
// 1. 从 PrincipalCollection 中来获取登录用户的信息
Object principal = principals.getPrimaryPrincipal();
// 2. 利用登录的用户的信息来用户当前用户的角色或权限(可能需要查询数据库)
Set<String> roles = new HashSet<>();
roles.add("admin");
List<String> permissions=new ArrayList<>();
permissions.add("admin
*");
/* if ("admin".equals(principal)) { roles.add("admin"); }*/
// 3. 创建 SimpleAuthorizationInfo, 并设置其 roles 属性.
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//添加角色
info.addRoles(roles);
//添加权限
info.addStringPermissions(permissions);
// 4. 返回 SimpleAuthorizationInfo 对象.
return info;
}
MyRealm
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("MyRealm Authorization");
Object principal = principals.getPrimaryPrincipal();
Set<String> roles = new HashSet<>();
roles.add("user");
Set<String> permissions = new HashSet<>();
permissions.add("user:view:*");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(roles);
info.addStringPermissions(permissions);
return info;
}
jsp 页面校验
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
<% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path; %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<h1>Hi ~~shiro</h1>
<h4>This Subject has role of User</h4>
<!-- 只有admin 角色才能显示一下内容 -->
<shiro:hasRole name="admin">
Only admin can access to those words
</shiro:hasRole>
<br>
<br>
<!-- 只有user 角色才能显示一下内容 -->
<shiro:hasRole name="user">
Only User can access to those words
</shiro:hasRole>
<br>
<br>
<shiro:hasRole name="Manager">
Only Manager can access to those words
</shiro:hasRole>
<br>
<br>
<!-- 只有 user:view:* 权限才能显示一下内容 -->
<shiro:hasPermission name="user:view:*">
Only User has 'user:view:*' can access to those words
</shiro:hasPermission>
<br>
<br>
<!-- 只有 admin:view:* 权限才能显示一下内容 -->
<shiro:hasPermission name="admin:view:*">
Only Admin has 'admin:view:*' can access to those words
</shiro:hasPermission>
<br>
<br>
<shiro:hasPermission name="manager:view:*">
Only Manager has 'manager:view:*' can access to those words
</shiro:hasPermission>
<br>
<br>
<a href="<%=basePath%>/login/logout">Logout</a>
</body>
</html>
校验结果
Permission 鉴权的方式
在之前的学习中我们了解到了 Shiro 的Authorization有三种方式,作为 细粒度化的 Authorization,Permission 同样也支持粗粒度的 Authorization 的三种方式即代码判断,注解,JSP页面校验。
代码判断/注解
@RequiresRoles("admin")
@RequiresPermissions("admin
*")
@RequestMapping(value="/admin")
public String AuthorizationOne () {
Subject admin =SecurityUtils.getSubject();
System.out.println("角色 " + admin.getPrincipal());
admin.isPermitted("admin
*");
System.out.println("角色 " + admin.getPrincipal()+" 是否拥有 admin
* 权限:"+admin.isPermitted("admin
*"));
return "admin";
}
@RequiresRoles("user")
@RequiresPermissions("user
*")
@RequestMapping(value="/user")
public String AuthorizationTwo(){
Subject user =SecurityUtils.getSubject();
System.out.println("角色 " + user.getPrincipal());
user.isPermitted("user
*");
System.out.println("角色 " + user.getPrincipal()+" 是否拥有 user
* 权限:"+user.isPermitted("user
*"));
return "user";
}
以上代码体现了 Shiro 在做细粒度化鉴权 Permission时使用到的两种方式:代码判断个注解。
JSP 页面校验
<!-- 只有 user
* 权限才能显示一下内容 -->
<shiro:hasPermission name="user
*">
Only User has 'user
*' can access to those words
</shiro:hasPermission>
<br>
<br>
<!-- 只有 admin
* 权限才能显示一下内容 -->
<shiro:hasPermission name="admin
*">
Only Admin has 'admin
*' can access to those words
</shiro:hasPermission>
<br>
<br>
<shiro:hasPermission name="manager
*">
Only Manager has 'manager
*' can access to those words
</shiro:hasPermission>
<br>
<br>
小结
Shiro Authorization 大致可以被概述为实现了角色授权和权限授权。
- 角色授权:粗粒度授权,为当前Subject 做角色判定或赋予。
- 权限授权:细粒度授权,为当前Role 做权限判定或赋予。
- 在细粒度授权时要重分理解 资源标识符:操作:对象实例 ID 规则的定义的应用的实际场景。在我们自己做实际细粒度授权Permission时,这将我们提供很好的解决方案。
还没有评论,来说两句吧...