03-3 shiro授权
一、授权概述
- 授权也称访问控制,应用中控制资源访问权限
- 主体:访问应用的用户
- 资源:应用中用户可以访问的任何数据
- 权限:表示是否可以访问某个数据或资源
- 角色:代表为操作集合,可以理解为权限的集合
- 隐式角色:直接通过角色来验证用户是否有操作权限
- 显式角色:通过权限控制用户访问资源
授权的三种方式:
Subject subject = SecurityUtils.getSubject();
boolean add = subject.hasRole("add");
@RequireRoles("add")
public void add(){
//有权限操作
}
<shiro:hasRole name="add">
//有权限操作
</shiro:hasRole>
二、授权验证
1、创建项目:01-role
2、添加配置文件:shiro_role.ini
[users]
zhang=123,role1,role2
wang=123,role1
3、授权校验
package com.role;
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.junit.Test;
import java.util.Arrays;
/**
* 授权验证
*
* @author brusion
* @date 2018/9/15
*/
public class RoleApplication {
@Test
public void roleTest() {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro_role.ini");
SecurityManager manager = factory.getInstance();
SecurityUtils.setSecurityManager(manager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
try {
subject.login(token);
System.out.println("=== 登录成功 === ");
boolean hasRole = subject.hasRole("role1");
System.out.println("=== 是否有 role1 权限 === " + hasRole);
boolean[] roles = subject.hasRoles(Arrays.asList("role1", "role2", "role3"));
for (int x = 0; x < roles.length; x++) {
System.out.println("=== 是否有 role" + x + " 权限 " + roles[x]);
}
boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1", "role2", "role3"));
System.out.println("=== 是否同时拥有 role1,role2,role3 权限 === " + hasAllRoles);
} catch (Exception e) {
System.out.println("=== 登录失败 === ");
}
subject.logout();
}
}
说明:
- Subject#hasRole:单个权限验证,返回为boolean类型
- Subject#hasRoles:多个权限验证,返回值为每个权限校验的数组
- Subject#hasAllRoles:是否同时拥有多个权限,返回值为boolean类型
- Subject#checkRole:同样有3个方法,使用基本一致,不同的是checkRole没有权限会抛出异常
三、对资源的控制
四、授权流程
4.1、授权流程
- 1、调用Subject#isPermitted/hasRole接口,并委托给SecurityManager。而SecurityManager委托给Authorizer。(Authorizer是真正的授权者)
- 2、Authorizer通过PermissionResolver吧字符串账号成对应的Permission实例。
- 3、授权之前会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限
- 4、Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个会委托给ModularRealmAuthorizer进行循环判断。
4.2、ModularRealmAuthorizer多Realm匹配流程
- 1、先检查相应的Realm是否实现了Authorizer
- 2、如果实现了Authorizer调用相应的isPermitted/hasRole接口进行匹配
- 3、如果有一个realm匹配将返回true,负责返回false
4.3、Realm授权流程
- 1、如果调用hasRole,则直接获取AuthorizerInfo#getRoles与传入的角色比较即可
- 2、如果调用如isPermitted(""),首先通过PermissionResolver将权限字符串转换成相应的实例,默认使用WildcardPermissionResolver,转换为通配符WildcardPermission
- 3、通过AuthorizationInfo#getObjectPermissions得到Permission实例集合,通过通过获取用户的角色并通过RolePermissionResolver解析角色对应的权限。
- 4、调用Permission#implies(Permission)逐个与传入的权限比较,匹配的权限返回true,负责返回false。
五、自定义RolePermissionResolver
5.1、创建项目:03-authorizer
5.2、创建类实现Permission定义权限规则
package com.authorizer.permission;
import com.alibaba.druid.util.StringUtils;
import org.apache.shiro.authz.Permission;
/**
* 位移方式的权限
* 规则
* 权限字符串格式:+资源字符串+权限位+实例ID
* 1、以+开头之间通过+分割
* 2、权限:
* 0 表示所有权限
* 1 新增 0001
* 2 修改 0010
* 4 删除 0100
* 8 查看 1000
* 如 +user+10 表示对资源user拥有修改/查看权限
* 不考虑一些异常情况
*
* @author brusion
* @date 2018/9/16
*/
public class BitPermission implements Permission {
private String resourceIdentify;
private int permissionBit;
private String instanceId;
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;
}
@Override
public String toString() {
return "BitPermission{" +
"resourceIdentify='" + resourceIdentify + '\'' +
", permissionBit=" + permissionBit +
", instanceId='" + instanceId + '\'' +
'}';
}
}
5.3、创建类实现权限解析
package com.authorizer.permission;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.PermissionResolver;
import org.apache.shiro.authz.permission.WildcardPermission;
/**
* @author brusion
* @date 2018/9/16
*/
public class AuthorPermissionResolver implements PermissionResolver {
@Override
public Permission resolvePermission(String string) {
//根据权限字符串是否以+开头来解析权限字符串为BitPermission或WildcardPermission
if (string.startsWith("+")) {
return new BitPermission(string);
}
return new WildcardPermission(string);
}
}
5.4、创建类实现权限集合
package com.authorizer.permission;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.RolePermissionResolver;
import org.apache.shiro.authz.permission.WildcardPermission;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* @author brusion
* @date 2018/9/16
*/
public class AuthorRolePermission implements RolePermissionResolver {
@Override
public Collection<Permission> resolvePermissionsInRole(String string) {
//根据角色字符串来解析得到的权限集合
if ("role1".equals(string)) {
List<Permission> permissions = Arrays.asList((Permission) new WildcardPermission("menu:*"));
System.out.println("--- permissions权限集合是: " + permissions.toString());
return permissions;
}
return null;
}
}
5.5、创建类用于提供用户数据和用户权限
- 推荐使用AuthorizingRealm代替Realm,只需要复写内部方法即可
package com.authorizer.realm;
import com.authorizer.permission.BitPermission;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.authz.permission.WildcardPermission;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* @author brusion
* @date 2018/9/16
*/
public class AuthorizerRealm extends AuthorizingRealm {
//推荐使用AuthorizingRealm代替Realm,只需要复写内部方法即可
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//根据用户身份获取授权信息
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addRole("role1");
authorizationInfo.addRole("role2");
authorizationInfo.addObjectPermission(new BitPermission("+user1+10"));
authorizationInfo.addObjectPermission(new WildcardPermission("user1:*"));
authorizationInfo.addStringPermission("+user2+10");
authorizationInfo.addStringPermission("user2:*");
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取身份验证信息
String username = (String) token.getPrincipal(); //得到用户名
String password = new String((char[]) token.getCredentials()); //得到密码
if (!"zhang".equals(username)) {
throw new UnknownAccountException(); //如果用户名错误
}
if (!"123".equals(password)) {
throw new IncorrectCredentialsException(); //如果密码错误
}
//如果身份认证验证成功,返回一个AuthenticationInfo实现;
return new SimpleAuthenticationInfo(username, password, getName());
}
}
5.6、创建ini配置文件:shiro_authorizer.ini
[main]
#自定义authorizer
authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
#自定义permissionResolver
#permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolver
permissionResolver=com.authorizer.permission.AuthorPermissionResolver
authorizer.permissionResolver=$permissionResolver
#自定义rolePermissionResolver
rolePermissionResolver=com.authorizer.permission.AuthorRolePermission
authorizer.rolePermissionResolver=$rolePermissionResolver
securityManager.authorizer=$authorizer
#自定义realm 一定要放在securityManager.authorizer赋值之后
#(因为调用setRealms会将realms设置给authorizer,并给各个Realm设置permissionResolver和rolePermissionResolver)
realm=com.authorizer.realm.AuthorizerRealm
securityManager.realms=$realm
5.7、验证类
package com.authorizer;
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.junit.Test;
/**
* @author brusion
* @date 2018/9/16
*/
public class AuthorizerApplication {
@Test
public void checkPermission() {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro_authorizer.ini");
SecurityManager manager = factory.getInstance();
SecurityUtils.setSecurityManager(manager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
try {
subject.login(token);
System.out.println(" 登录成功 ");
//判断是否拥有权限
boolean update1 = subject.isPermitted("user1:update");
System.out.println("是否有权限 user1:update " + update1);
boolean update2 = subject.isPermitted("user2:update");
System.out.println("是否有权限 user2:update " + update2);
//二进制方式权限判断
System.out.println("是否有权限 +user1+1 " + subject.isPermitted("+user1+1"));
System.out.println("是否有权限 +user1+8 " + subject.isPermitted("+user1+8"));
System.out.println("是否有权限 +user2+10 " + subject.isPermitted("+user2+10"));
System.out.println("是否有权限 +user1+4 " + subject.isPermitted("+user1+4"));
System.out.println("是否有权限 menu:view " + subject.isPermitted("menu:view"));
} catch (Exception e) {
System.out.println(" 登录失败 " + e.toString());
}
subject.logout();
}
}
5.8运行效果
登录成功
--- permissions权限集合是: [[menu]:[*]]
是否有权限 user1:update true
--- permissions权限集合是: [[menu]:[*]]
是否有权限 user2:update true
--- permissions权限集合是: [[menu]:[*]]
是否有权限 +user1+1 false
--- permissions权限集合是: [[menu]:[*]]
是否有权限 +user1+8 true
--- permissions权限集合是: [[menu]:[*]]
是否有权限 +user2+10 true
--- permissions权限集合是: [[menu]:[*]]
是否有权限 +user1+4 false
--- permissions权限集合是: [[menu]:[*]]
是否有权限 menu:view true