鲁春利的工作笔记,好记性不如烂笔头
Shiro配置文件(shiro-authorize-permission.ini)
[main] # 定义变量 # 变量名=全类名 [users] # 用户名=密码,角色1,角色2,...,角色N lucl=123,role1,role2 zs=123,role1 [roles] # 角色=权限1,权限2,...,权限N role1=user:create,user:update role2=user:create,user:delete
单元测试
/** * 基于资源的访问控制 */ @Test public void testWhetherHasPermission () { // 1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro/authorize/shiro-authorize-permission.ini"); // 2、得到SecurityManager实例并绑定给SecurityUtils org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); // 3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证) Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("lucl", "123"); try{ // 4、登录,即身份验证 subject.login(token); } catch (AuthenticationException e) { // 5、身份验证失败 logger.info("用户身份验证失败"); e.printStackTrace(); } // 用户身份得到确认 if (subject.isAuthenticated()) { logger.info("用户登录成功。"); /** * 进行权限判断 */ // 判断拥有权限:user:create Assert.assertTrue(subject.isPermitted("user:create")); // 判断拥有权限:user:update and user:delete Assert.assertTrue(subject.isPermittedAll("user:update", "user:delete")); // 判断没有权限:user:view Assert.assertFalse(subject.isPermitted("user:view")); // 断言拥有权限:user:create subject.checkPermission("user:create"); // 断言拥有权限:user:delete and user:update subject.checkPermissions("user:delete", "user:update"); // 断言拥有权限:user:view 失败抛出异常 subject.checkPermissions("user:view"); } else { logger.info("用户登录失败。"); } // 6、退出 subject.logout(); }
isPermitted过程
subject.isPermitted
package org.apache.shiro.subject.support; public class DelegatingSubject implements Subject { /** hasPrincipals()判断身份信息,身份信息是在login方法登录后赋值的 */ public boolean isPermitted(String permission) { return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission); } public void login(AuthenticationToken token) throws AuthenticationException { clearRunAsIdentitiesInternal(); /** 身份认证 */ Subject subject = securityManager.login(this, token); PrincipalCollection principals; String host = null; if (subject instanceof DelegatingSubject) { DelegatingSubject delegating = (DelegatingSubject) subject; //we have to do this in case there are assumed identities - we don't want to lose the 'real' principals: principals = delegating.principals; host = delegating.host; } else { principals = subject.getPrincipals(); } if (principals == null || principals.isEmpty()) { // 异常信息 throw new IllegalStateException(msg); } this.principals = principals; this.authenticated = true; // 代码略 } }
securityManager.isPermitted
securityManager的实现为DefaultSecurityManager,但DefaultSecurityManager无isPermitted方法。
org.apache.shiro.mgt.DefaultSecurityManager extends org.apache.shiro.mgt.SessionsSecurityManager extends org.apache.shiro.mgt.AuthorizingSecurityManager extends org.apache.shiro.mgt.AuthenticatingSecurityManager
调用AuthorizingSecurityManager的isPermitted方法
package org.apache.shiro.mgt; public abstract class AuthorizingSecurityManager extends AuthenticatingSecurityManager { /** * The wrapped instance to which all of this SecurityManager authorization calls are delegated. * 授权的核心接口 */ private Authorizer authorizer; public AuthorizingSecurityManager() { super(); this.authorizer = new ModularRealmAuthorizer(); } public boolean isPermitted(PrincipalCollection principals, Permission permission) { return this.authorizer.isPermitted(principals, permission); } // 其他代码略 }
ModularRealmAuthorizer.isPermitted方法
package org.apache.shiro.authz; public class ModularRealmAuthorizer implements Authorizer, PermissionResolverAware, RolePermissionResolverAware { /** * The realms to consult during any authorization check. */ protected Collection<Realm> realms; protected PermissionResolver permissionResolver; protected RolePermissionResolver rolePermissionResolver; public ModularRealmAuthorizer() { // 无参构造方法 } public ModularRealmAuthorizer(Collection<Realm> realms) { setRealms(realms); } /* 权限判断 */ public boolean isPermitted(PrincipalCollection principals, Permission permission) { assertRealmsConfigured(); for (Realm realm : getRealms()) { if (!(realm instanceof Authorizer)) continue; if (((Authorizer) realm).isPermitted(principals, permission)) { return true; } } return false; } }
转了一圈,具体的isPermitted还是通过最终的realm来完成(IniRealm)
org.apache.shiro.realm.text.IniRealm extends org.apache.shiro.realm.text.TextConfigurationRealm extends org.apache.shiro.realm.SimpleAccountRealm extends org.apache.shiro.realm.AuthorizingRealm
AuthorizingRealm.isPermitted被调用
package org.apache.shiro.realm; /** * An AuthorizingRealm extends the AuthenticatingRealm's capabilities by adding Authorization (access control) support. * @see org.apache.shiro.authz.SimpleAuthorizationInfo * @since 0.2 */ public abstract class AuthorizingRealm extends AuthenticatingRealm implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware { private PermissionResolver permissionResolver; private RolePermissionResolver permissionRoleResolver; public AuthorizingRealm() { this(null, null); } public AuthorizingRealm(CacheManager cacheManager) { this(cacheManager, null); } public AuthorizingRealm(CredentialsMatcher matcher) { this(null, matcher); } public AuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher) { /** super()会调用父类的无参构造方法,也就是new AuthenticatingRealm() {this(null, new SimpleCredentialsMatcher());} */ super(); if (cacheManager != null) setCacheManager(cacheManager); if (matcher != null) setCredentialsMatcher(matcher); this.authorizationCachingEnabled = true; // permissionResolver的实现类,自定义Permission时自定了也自定义了这个东西 this.permissionResolver = new WildcardPermissionResolver(); // 代码略 } public boolean isPermitted(PrincipalCollection principals, String permission) { // WildcardPermissionResolver.resolvePermission {return new WildcardPermission(permissionString);} Permission p = getPermissionResolver().resolvePermission(permission); return isPermitted(principals, p); } public boolean isPermitted(PrincipalCollection principals, Permission permission) { /** * getAuthorizationInfo(principals)实际返回的是SimpleAccount; * 如果我们在自定义的Realm中实现了info.add("system:edit:1"),那么SimpleAccount就获取到了其拥有的权限列表: * 说明:也就是shiro不维护权限信息,其应该具有的权限信息是由业务系统根据实际情况来设定的 * isPermitted会比较传入的权限字符串,是否在实际设定的权限列表中(权限列表一般根据登录用户权限从数据库中读取并加载) */ AuthorizationInfo info = getAuthorizationInfo(principals); return isPermitted(permission, info); } /** 最终的判断方法(权限集合是在Realm实现时添加的权限列表,如info.add("system:edit:1")) */ protected boolean isPermitted(Permission permission, AuthorizationInfo info) { Collection<Permission> perms = getPermissions(info); if (perms != null && !perms.isEmpty()) { for (Permission perm : perms) { /** Permission的implies方法总算是被调用到了 */ if (perm.implies(permission)) { return true; } } } return false; } protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) { if (principals == null) { return null; } AuthorizationInfo info = null; // 代码略,主要实现从缓存中获取授权数据 if (info == null) { // Call template method if the info was not found in a cache /** 这是最核心的授权实现方法,用户自定义方法一般重写该方法,实现自己的授权过程 */ info = doGetAuthorizationInfo(principals); // If the info is not null and the cache has been created, then cache the authorization info. // 代码略,获取后重新加入缓存中 } return info; } // 该类的doGetAuthorizationInfo方法为抽象方法,需要子类根据身份信息实现自己的授权 protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals); }
SimpleAccountRealm.doGetAuthorizationInfo方法
package org.apache.shiro.realm; public class SimpleAccountRealm extends AuthorizingRealm { // 部分代码略 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = getUsername(principals); USERS_LOCK.readLock().lock(); try { return this.users.get(username); // 返回SimpleAccount } finally { USERS_LOCK.readLock().unlock(); } } }