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

来源:互联网 发布:mac谷歌翻墙教程 编辑:程序博客网 时间:2024/06/01 08:58

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

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

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

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

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

authorizer=org.apache.shiro.authz.ModularRealmAuthorizersecurityManager.authorizer=$authorizer

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

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

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

rolePermissionResolver=com.github.zhangkaitao.shiro.chapter3.permission.MyRolePermissionResolverauthorizer.rolePermissionResolver=$rolePermissionResolver
示例:

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

[main]#自定义authorizerauthorizer=org.apache.shiro.authz.ModularRealmAuthorizer#自定义permissionResolver#permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolverpermissionResolver=chapter3.authorizer.BitAndWildPermissionResolverauthorizer.permissionResolver=$permissionResolver#自定义rolePermissionResolverrolePermissionResolver=chapter3.authorizer.MyRolePermissionResolverauthorizer.rolePermissionResolver=$rolePermissionResolversecurityManager.authorizer=$authorizer#自定义realm一定要放在securityManager.authorizer赋值之后(因为调用setRealms会将realms设置给authorizer,并给各个Realm设置permissionResolver和rolePermissionResolver)realm=chapter3.authorizer.MyRealmsecurityManager.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;//实例IDprivate 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{@Overridepublic 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{@Overridepublic 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{@Testpublic void testIsPermitted(){login("classpath:shiro-authorizer.ini","zhang","123");Subject subject = SecurityUtils.getSubject();//判断拥有权限:user:createAssert.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配置文件初始化SecurityManagerFactory<SecurityManager> factory = new IniSecurityManagerFactory(configFile);//2. 得到SecurityManager实例,并绑定给SecurityUtilsSecurityManager 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]#自定义authorizerauthorizer=org.apache.shiro.authz.ModularRealmAuthorizer#自定义permissionResolver#permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolverpermissionResolver=chapter3.authorizer.BitAndWildPermissionResolverauthorizer.permissionResolver=$permissionResolver#自定义rolePermissionResolverrolePermissionResolver=chapter3.authorizer.MyRolePermissionResolverauthorizer.rolePermissionResolver=$rolePermissionResolversecurityManager.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.JdbcRealmdataSource=com.mchange.v2.c3p0.ComboPooledDataSourcedataSource.driverClass=com.mysql.jdbc.DriverdataSource.jdbcUrl=jdbc:mysql://localhost:3306/shirodataSource.user=rootdataSource.password=123456jdbcRealm.dataSource=$dataSourcejdbcRealm.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()//判断权限匹配

原创粉丝点击