pangxia75 发表于 2018-11-19 12:28:25

Apache Shiro学习笔记(三)用户授权isPermitted过程

鲁春利的工作笔记,好记性不如烂笔头
  

  Shiro配置文件(shiro-authorize-permission.ini)

# 定义变量
# 变量名=全类名

# 用户名=密码,角色1,角色2,...,角色N
lucl=123,role1,role2
zs=123,role1

# 角色=权限1,权限2,...,权限N
role1=user:create,user:update
role2=user:create,user:delete  

  单元测试
/**
* 基于资源的访问控制
*/
@Test
public void testWhetherHasPermission () {
    // 1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
    Factory 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 realms;
    protected PermissionResolver permissionResolver;
    protected RolePermissionResolver rolePermissionResolver;
    public ModularRealmAuthorizer() {
      // 无参构造方法
    }
    public ModularRealmAuthorizer(Collection 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 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();
      }
    }
}  




页: [1]
查看完整版本: Apache Shiro学习笔记(三)用户授权isPermitted过程