第三章 授权(三)Authorizer、PermissionResolver及RolePermissionResolver(自定义Realm+JDBCRealm)

Authorizer的职责是进行授权(访问控制),是Shiro API中授权核心的入口点,其提供了相应的角色/权限判断接口,具体请参考其Javadoc。

SecurityManager 继承了Authorizer 接口,且提供了ModularRealmAuthorizer用于多Realm时的授权匹配。

PermissionResolver用于解析权限字符串到Permission实例。

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

我们可以通过如下ini配置更改Authorizer实现——ModularRealmAuthorizer

authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
securityManager.authorizer=$authorizer

对于ModularRealmAuthorizer,相应的AuthorizingSecurityManager 会在初始化完成后自动将相应的realm 设置进去,我们也可以通过调用其setRealms()方法进行设置。对于实现自己的authorizer可以参考ModularRealmAuthorizer实现即可,在此就不提供示例了。

设置ModularRealmAuthorizer的permissionResolver,其会自动设置到相应的Realm上(其实现了PermissionResolverAware接口),如:

permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolver
authorizer.permissionResolver=$permissionResolver
设置ModularRealmAuthorizer 的 rolePermissionResolver,其会自动设置到相应的Realm 上(其实现了RolePermissionResolverAware接口),如:

rolePermissionResolver=com.github.zhangkaitao.shiro.chapter3.permission.MyRolePermission
Resolver
authorizer.rolePermissionResolver=$rolePermissionResolver
示例:

1. ini配置(shiro-authorizer.ini)

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

securityManager.authorizer=$authorizer

#自定义realm一定要放在securityManager.authorizer赋值之后(因为调用setRealms会将realms设置给authorizer,并给各个Realm设置permissionResolver和rolePermissionResolver)
realm=chapter3.authorizer.MyRealm
securityManager.realms=$realm
#另外,不能使用IniSecurityManagerFactory创建的IniRealm,因为其初始化顺序的问题可能造成后续的初始化Permission造成影响。

设置securityManager 的realms 一定要放到最后,因为在调用SecurityManager.setRealms时会将realms 设置给authorizer ,并为各个Realm 设置permissionResolver 和rolePermissionResolver。另外,不能使用IniSecurityManagerFactory创建的IniRealm,因为其初始化顺序的问题可能造成后续的初始化Permission造成影响。

2. 定义BitAndWildPermissionResolver及BitPermission

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

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

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

如+user+10 表示对资源user拥有修改/查看权限。

public class BitPermission implements Permission{
	//资源字符串
	private String resourceIdentify;
	//权限位
	private int permissionBit;
	//实例ID
	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 = "*";
		}
	}
	/*
	 * Permission接口提供了boolean implies(Permission p)方法用于判断权限匹配的
	 */
	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;
	}
}

Permission接口提供了boolean implies(Permission p)方法用于判断权限匹配的;

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

BitAndWildPermissionResolver 实现了PermissionResolver 接口,并根据权限字符串是否以“+”开头来解析权限字符串为BitPermission或WildcardPermission。

3. 定义MyRolePermissionResolver

RolePermissionResolver用于根据角色字符串来解析得到权限集合。

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{
	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;
	}
	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());
	}
}

此时我们继承AuthorizingRealm 而不是实现Realm 接口;推荐使用AuthorizingRealm,因为:

AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token):表示获取身份验证信息;

AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals):表示根据用户身份获取授权信息。

这种方式的好处是当只需要身份验证时只需要获取身份验证信息而不需要获取授权信息。对于AuthenticationInfo和AuthorizationInfo请参考其Javadoc 获取相关接口信息。

注意:此次还要注意就是不能把我们自定义的如“+user1+10”配置到INI 配置文件,即使有IniRealm 完成,因为IniRealm 在new 完成后就会解析这些权限字符串,默认使用了WildcardPermissionResolver 完成,即此处是一个设计权限,如果采用生命周期(如使用初始化方法)的方式进行加载就可以解决我们自定义permissionResolver的问题。

5. 测试用例

public class AuthorizerTest{
	@Test
	public void testIsPermitted(){
		login("classpath:shiro-authorizer.ini","zhang","123");
		Subject subject = SecurityUtils.getSubject();
		//判断拥有权限: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"));//新增及查看
		Assert.assertFalse(subject.isPermitted("+user1+4"));//没有删除权限
		Assert.assertTrue(subject.isPermitted("menu:view"));//通过MyRolePermissionResolver解析得到的权限 
	}
	public void login(String configFile,String username,String password){
		//1. 获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
		Factory<SecurityManager> factory = new IniSecurityManagerFactory(configFile);
		//2. 得到SecurityManager实例,并绑定给SecurityUtils
		SecurityManager securityManager = factory.getInstance();
		SecurityUtils.setSecurityManager(securityManager);
		//3. 得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken(username,password);
		
		subject.login(token);
	}
}
通过如上步骤可以实现自定义权限验证了。另外因为不支持hasAnyRole/isPermittedAny 这种方式的授权,可以参考我的一篇《简单shiro 扩展实现NOT、AND、OR 权限验证》进行简单的扩展完成这个需求,在这篇文章中通过重写AuthorizingRealm 里的验证逻辑实现的。


=============================================================================================

将自定义的realms转为JdbcRealm

shiro-authorizer.ini

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

securityManager.authorizer=$authorizer

#自定义realm一定要放在securityManager.authorizer赋值之后(因为调用setRealms会将realms设置给authorizer,并给各个Realm设置permissionResolver和rolePermissionResolver)
#realm=chapter3.authorizer.MyRealm
#securityManager.realms=$realm
#另外,不能使用IniSecurityManagerFactory创建的IniRealm,因为其初始化顺序的问题可能造成后续的初始化Permission造成影响。

jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
dataSource.driverClass=com.mysql.jdbc.Driver
dataSource.jdbcUrl=jdbc:mysql://localhost:3306/shiro
dataSource.user=root
dataSource.password=123456
jdbcRealm.dataSource=$dataSource
jdbcRealm.permissionsLookupEnabled=true	#开启权限查询
securityManager.realms=$jdbcRealm

=============================================================================================





自己理解的:

Assert.assertTrue(subject.isPermitted("user1:update"));

authorizationInfo.addRole("role1");

MyRealm doGetAuthenticationInfo()// 身份认证

BitAndWildPermissionResolver resolvePermission() //权限字符串解析成Permission

MyRealm doGetAuthorizationInfo()//获取授权信息(有角色,有权限)

(有角色则:)MyRolePermissionResolver resolvePermissionsInRole()//从角色映射权限(从角色到权限)

BitPermission implies() //权限匹配


(+user2+10)

BitPermission BitPermission()//创建Permission对象 —— 可能是被验证权限字符串

MyRealm doGetAuthorizationInfo()//获取授权信息(有角色,有权限)

BitAndWildPermissionResolver resolvePermission() //权限字符串解析成Permission

BitPermission BitPermission()//创建Permission对象 —— 可能是验证权限字符串

BitPermissionimplies()//判断权限匹配

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值