权限认证

权限认证流程

对 isPermitted 进行分析

     public static void main(String[] args) {
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            SecurityManager securityManager = factory.getInstance();
            SecurityUtils.setSecurityManager(securityManager);

            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken();
            usernamePasswordToken.setUsername("root");
            usernamePasswordToken.setPassword("secret".toCharArray());

            subject.login(usernamePasswordToken);

            System.out.println("访问权限认证:"+subject.isPermitted("user:myview"));
     }

subject.isPermitted(“user:myview”)

isPermitted时序图

DelegatingSubject

    public boolean isPermitted(String permission) {
        return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission);
    }

级别2

hasPrincipals()

权限列表是不是为空

DelegatingSubject

    public PrincipalCollection getPrincipals() {
        return CollectionUtils.isEmpty(this.runAsPrincipals) ? this.principals : this.runAsPrincipals.get(0);
    }

    protected boolean hasPrincipals() {
        return !CollectionUtils.isEmpty(getPrincipals());
    }

级别3

securityManager.isPermitted(getPrincipals(), permission);

委托给 securityManager 进行权限判断

AuthorizingSecurityManager


    public boolean isPermitted(PrincipalCollection principals, String permissionString) {
        return this.authorizer.isPermitted(principals, permissionString);
    }

级别4

securityManager.isPermitted(getPrincipals(), permission);

委托给 authorizer 进行权限判断

AuthorizingSecurityManager


    public boolean isPermitted(PrincipalCollection principals, String permissionString) {
        return this.authorizer.isPermitted(principals, permissionString);
    }

级别5

this.authorizer.isPermitted(principals, permissionString);

用所有的realm进行认证,第一个realm认证成功就返回

ModularRealmAuthorizer

    public boolean isPermitted(PrincipalCollection principals, String permission) {
    // realm 为空就抛出异常
        assertRealmsConfigured();
        for (Realm realm : getRealms()) {
            if (realm.isPermitted(principals, permission)) {
                return true;
            }
        }
        return false;
    }

级别6

realm.isPermitted(principals, permission)

IniRealm 权限认证(IniRealm的间接父类AuthorizingRealm)

AuthorizingRealm

public boolean isPermitted(PrincipalCollection principals, String permission) {
        Permission p = getPermissionResolver().resolvePermission(permission);
        return isPermitted(principals, p);
    }

级别7-1

Permission p = getPermissionResolver().resolvePermission(permission);

AuthorizingRealm 构造器中初始化了permissionResolver,
返回通配符权限对象

AuthorizingRealm

    public AuthorizingRealm() {
          this.authorizationCachingEnabled = true;
          this.permissionResolver = new WildcardPermissionResolver();

          int instanceNumber = INSTANCE_COUNT.getAndIncrement();
          this.authorizationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
          if (instanceNumber > 0) {
              this.authorizationCacheName = this.authorizationCacheName + "." + instanceNumber;
          }
     }


    public PermissionResolver getPermissionResolver() {
        return permissionResolver;
    }

PermissionResolver

 public Permission resolvePermission(String permissionString) {
 // 参见  [通配符权限初始化]
        return new WildcardPermission(permissionString);
    }

级别7-2

return isPermitted(principals, p);

  1. 通过凭证(PrincipalCollection) 获取认证信息
  2. 通过认证信息(AuthorizationInfo) 获取权限列表 (Collection)
  3. 遍历权限列表,当权限能匹配目标权限时,返回true. 全部匹配失败就返回false
    public boolean isPermitted(PrincipalCollection principals, Permission permission) {
          // 参见 [realm 通过凭证获取认证信息]
          AuthorizationInfo info = getAuthorizationInfo(principals);
          return isPermitted(permission, info);
    }

    private boolean isPermitted(Permission permission, AuthorizationInfo info) {
         # 参见 [Realm 通过认证信息获取权限集合]
          Collection<Permission> perms = getPermissions(info);
          if (perms != null && !perms.isEmpty()) {
              for (Permission perm : perms) {
                    # 参见 [Permission 权限匹配]
                  if (perm.implies(permission)) {
                      return true;
                  }
              }
          }
          return false;
      }

附录

通配符权限初始化

通配符权限解析器 WildcardPermissionResolver 实现了 PermissionResolver

级别1 创建通配符权限对象

public class WildcardPermissionResolver implements PermissionResolver {

    /**
     * Returns a new {@link WildcardPermission WildcardPermission} instance constructed based on the specified
     * <tt>permissionString</tt>.
     *
     * @param permissionString the permission string to convert to a {@link Permission Permission} instance.
     * @return a new {@link WildcardPermission WildcardPermission} instance constructed based on the specified
     *         <tt>permissionString</tt>
     */
    public Permission resolvePermission(String permissionString) {
        return new WildcardPermission(permissionString);
    }
}

级别2 创建通配符权限对象

return new WildcardPermission(permissionString);

将通配符对象的this.parts 按照冒号分割,在进行填充

WildcardPermission

    protected static final boolean DEFAULT_CASE_SENSITIVE = false;
    // 默认大小写敏感: DEFAULT_CASE_SENSITIVE=false
    public WildcardPermission(String wildcardString) {
        this(wildcardString, DEFAULT_CASE_SENSITIVE);
    }

    public WildcardPermission(String wildcardString, boolean caseSensitive) {
        setParts(wildcardString, caseSensitive);
    }

    protected static final String PART_DIVIDER_TOKEN = ":";

    protected static final String SUBPART_DIVIDER_TOKEN = ",";

    protected void setParts(String wildcardString, boolean caseSensitive) {
            if (wildcardString == null || wildcardString.trim().length() == 0) {
                throw new IllegalArgumentException("Wildcard string cannot be null or empty. Make sure permission strings are properly formatted.");
            }

            wildcardString = wildcardString.trim();
            // PART_DIVIDER_TOKEN 是权限层级分割符 ':' , 例如 winnebago:drive:eagle5 将被分割为 [weinebago,drive,eagle5] 
            List<String> parts = CollectionUtils.asList(wildcardString.split(PART_DIVIDER_TOKEN));

            this.parts = new ArrayList<Set<String>>();
            for (String part : parts) {
                // 将每个权限级别用 ',' 分割, 事实上没有用, 数组长度一定为1,  参见[权限解析之逗号分割重复]
                Set<String> subparts = CollectionUtils.asSet(part.split(SUBPART_DIVIDER_TOKEN));
                // 大小写不敏感就转换为小写
                if (!caseSensitive) {
                    subparts = lowercase(subparts);
                }
                if (subparts.isEmpty()) {
                    throw new IllegalArgumentException("Wildcard string cannot contain parts with only dividers. Make sure permission strings are properly formatted.");
                }
                this.parts.add(subparts);
            }

            if (this.parts.isEmpty()) {
                throw new IllegalArgumentException("Wildcard string cannot contain only dividers. Make sure permission strings are properly formatted.");
            }
        }

权限解析之逗号分割重复

调用堆栈

方法调用方法
TextConfigurationRealmprocessRoleDefinitions(Map
    public static Set<Permission> resolveDelimitedPermissions(String s, PermissionResolver permissionResolver) {
        Set<String> permStrings = toPermissionStrings(s);
        return resolvePermissions(permStrings, permissionResolver);
    }

    // 通过逗号分割权限,  例如 schwartz = lightsaber:*,order:*  将被分割为 ["lightsaber:*","order:*"] 
    public static Set<String> toPermissionStrings(String permissionsString) {
        String[] tokens = StringUtils.split(permissionsString);
        if (tokens != null && tokens.length > 0) {
            return new LinkedHashSet<String>(Arrays.asList(tokens));
        }
        return null;
    }

级别2
在 WildcardPermission的setParts 又将 lightsaber:* 分割一次, order:* 分割一次。
事实上已经无法再次分割了

WildcardPermission

  protected void setParts(String wildcardString, boolean caseSensitive) {
         //TODO 省略 
         for (String part : parts) {
             Set<String> subparts = CollectionUtils.asSet(part.split(SUBPART_DIVIDER_TOKEN));
         }

Realm 通过凭证获取认证信息

AuthorizationInfo info = getAuthorizationInfo(principals);

  1. 判断凭证信息是否为空,为空就返回空
  2. 从缓存中获取认证信息,获取到就返回
  3. 调用模板方法 doGetAuthorizationInfo,获取认证信息. 如果缓存不为空,并且认证信息也不为空 就将认证信息和凭证加入到缓存

AuthorizingRealm

protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {

        if (principals == null) {
            return null;
        }

        AuthorizationInfo info = null;

        if (log.isTraceEnabled()) {
            log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
        }

        Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
        if (cache != null) {
            if (log.isTraceEnabled()) {
                log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
            }
            Object key = getAuthorizationCacheKey(principals);
            info = cache.get(key);
            if (log.isTraceEnabled()) {
                if (info == null) {
                    log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");
                } else {
                    log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");
                }
            }
        }


        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.
            if (info != null && cache != null) {
                if (log.isTraceEnabled()) {
                    log.trace("Caching authorization info for principals: [" + principals + "].");
                }
                Object key = getAuthorizationCacheKey(principals);
                cache.put(key, info);
            }
        }

        return info;
    }

级别2-1

Cache

    public Cache<Object, AuthorizationInfo> getAuthorizationCache() {
            return this.authorizationCache;
    }

    public boolean isCachingEnabled() {
        return cachingEnabled;
    }

    public boolean isAuthorizationCachingEnabled() {
        return isCachingEnabled() && authorizationCachingEnabled;
    }

    private Cache<Object, AuthorizationInfo> getAvailableAuthorizationCache() {
        Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
        if (cache == null && isAuthorizationCachingEnabled()) {
            cache = getAuthorizationCacheLazy();
        }
        return cache;
    }

级别3

cache = getAuthorizationCacheLazy();

  1. 获取缓存管理器
    getCacheManager在父类CachingRealm 中定义.

CacheManager里面只定义了一个方法:
public

    // 延迟加载缓存信息
     private Cache<Object, AuthorizationInfo> getAuthorizationCacheLazy() {

            if (this.authorizationCache == null) {

                if (log.isDebugEnabled()) {
                    log.debug("No authorizationCache instance set.  Checking for a cacheManager...");
                }

                CacheManager cacheManager = getCacheManager();

                if (cacheManager != null) {
                    // 在AuthorizingRealm 的构造方法中初始化为:  this.authorizationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
                    // 在本例中的名称为: iniRealm.authorizationCache
                    String cacheName = getAuthorizationCacheName();
                    if (log.isDebugEnabled()) {
                        log.debug("CacheManager [" + cacheManager + "] has been configured.  Building " +
                                "authorization cache named [" + cacheName + "]");
                    }
                    this.authorizationCache = cacheManager.getCache(cacheName);
                } else {
                    if (log.isInfoEnabled()) {
                        log.info("No cache or cacheManager properties have been set.  Authorization cache cannot " +
                                "be obtained.");
                    }
                }
            }

            return this.authorizationCache;
        }

级别2-2

info = doGetAuthorizationInfo(principals);

通过模板方法获取认证信息

  1. 获得 principals 的用户名
  2. 通过ini文件加载的用户和认证信息的 Map
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return this.users.get(getUsername(principals));
    }
    // 获取有效的权限,调用父类的getAvailablePrincipal
    protected String getUsername(PrincipalCollection principals) {
        return getAvailablePrincipal(principals).toString();
    }

级别3

return getAvailablePrincipal(principals).toString();

  1. 从realm的名称中获得当前的名称
  2. 获取到了就直接返回
  3. 没有获取到就获取 凭证信息principals 的realmPrincipals的values。 (返回的是一个不可修改的LinkedHashSet)

AuthorizingRealm

    protected Object getAvailablePrincipal(PrincipalCollection principals) {
        if (principals == null || principals.isEmpty()) {
            return null;
        }
        Object primary;
        // 从realm的名称中获得当前的名称
        Collection thisPrincipals = principals.fromRealm(getName());
        if (thisPrincipals != null && !thisPrincipals.isEmpty()) {
            primary = thisPrincipals.iterator().next();
        } else {
            //no principals attributed to this particular realm.  Fall back to the 'master' primary:
            primary = principals.getPrimaryPrincipal();
        }
        return primary;
    }

SimplePrincipalCollection

    public Collection fromRealm(String realmName) {
        if (realmPrincipals == null || realmPrincipals.isEmpty()) {
            return Collections.EMPTY_SET;
        }
        Set principals = realmPrincipals.get(realmName);
        if (principals == null || principals.isEmpty()) {
            principals = Collections.EMPTY_SET;
        }
        return Collections.unmodifiableSet(principals);
    }

Realm 通过认证信息获取权限集合

  1. 新建 Set permissions = new HashSet();
  2. 获取info的 authzInfo.getObjectPermissions(); 添加到 permissions
  3. 获取info的 authzInfo.getStringPermissions(); 在将获取的Collection 解析为 Collection 添加到 permissions
  4. 获取info的 authzInfo.getRoles(); 在将获取的Collection 解析为 Collection 添加到 permissions
  5. 为空就返回 emptySet, 不为空就返回不可修改的Set ( Collections.unmodifiableSet(permissions); )
private Collection<Permission> getPermissions(AuthorizationInfo info) {
        Set<Permission> permissions = new HashSet<Permission>();

        if (info != null) {
            Collection<Permission> perms = info.getObjectPermissions();
            if (!CollectionUtils.isEmpty(perms)) {
                permissions.addAll(perms);
            }
            perms = resolvePermissions(info.getStringPermissions());
            if (!CollectionUtils.isEmpty(perms)) {
                permissions.addAll(perms);
            }

            perms = resolveRolePermissions(info.getRoles());
            if (!CollectionUtils.isEmpty(perms)) {
                permissions.addAll(perms);
            }
        }

        if (permissions.isEmpty()) {
            return Collections.emptySet();
        } else {
            return Collections.unmodifiableSet(permissions);
        }
    }

Permission 权限匹配

if (perm.implies(permission)) {

  1. 判断 目标是不是 WildcardPermission ,不是就返回匹配失败
  2. 获取当前的parts进行遍历判断
  3. 如果当前的part不存在通配符,并且当前的part不是完全包含目标的part 就返回false
  4. 再次进行parts遍历判断,如果part 不能再通配符就返回false

匹配的三种形式
1. 完全匹配
2. 小于目标部件(thisPart 小于 targetPart)
3. 大于目标部件(thisPart 大于 targetPart)

WildcardPermission

    public boolean implies(Permission p) {
        // By default only supports comparisons with other WildcardPermissions
        if (!(p instanceof WildcardPermission)) {
            return false;
        }

        WildcardPermission wp = (WildcardPermission) p;

        List<Set<String>> otherParts = wp.getParts();

        int i = 0;
        for (Set<String> otherPart : otherParts) {
            // If this permission has less parts than the other permission, everything after the number of parts contained
            // in this permission is automatically implied, so return true
            // 如果当前权限比目标权限更少,那就说明必然包含后面的内容
            // 简而言之:   当前parts: lightsaber:* 必然包含目标part: lightsaber:a:b:c 就直接返回true
              // 路线2 : 小于目标部件
            if (getParts().size() - 1 < i) {
                return true;
            } else {
                // order:a:*:b 包含 order:a:c:b
                Set<String> part = getParts().get(i);
                if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) {
                    return false;
                }
                i++;
            }
        }
        // 如果当前权限多余目标权限,那么当前权限必须是 通配符 * 
        // 看例子 
        // order:a:b 不匹配 order:a
        // order:a:* 匹配 order:a
        // order:a:*:*:* 匹配  order:a, order:a:b
        // 虽然没有人写  order:a:*:*:*  的形式,但是从逻辑上来说这样写也可以
        // If this permission has more parts than the other parts, only imply it if all of the other parts are wildcards
          // 路线3 : 大于目标部件
        for (; i < getParts().size(); i++) {
            Set<String> part = getParts().get(i);
            if (!part.contains(WILDCARD_TOKEN)) {
                return false;
            }
        }
        // 路线1 : 完全匹配
        return true;
    }

匹配示例:

类型当前匹配符目标路径匹配结果
完全匹配 a:b:ca:b:c匹配
完全匹配 a:b:*a:b:c匹配
完全匹配x:yx:a不匹配
小于目标部件x:y:*x:y:z:d匹配
小于目标部件x:ya:b:d:e不匹配
小于目标部件a:*a:b:c:d:e不匹配
大于目标部件order:a:b:corder:a不匹配
大于目标部件order:a:*order:a匹配
大于目标部件 order:a:*:*:*order:a匹配

默认过滤器

  • 来源: https://blog.csdn.net/cheng43526899/article/details/53837635
























































    简称例子
    anon org.apache.shiro.web.filter.authc.AnonymousFilter /admins/**=anon 没有参数,表示可以匿名使用。
    authcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter/admins/user/**=authc表示需要认证(登录)才能使用,没有参数
    authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter/admins/user/**=authcBasic没有参数表示httpBasic认证
    permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter /admins/user/=perms[user:add:],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/=perms[“user:add:,user:modify:*”],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
    portorg.apache.shiro.web.filter.authz.PortFilter/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。
    restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilter/admins/user/=rest[user],根据请求的方法,相当于/admins/user/=perms[user:method] ,其中method为post,get,delete等。
    rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter/admins/user/=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/=roles[“admin,guest”],每个参数通过才算通过,相当于hasAllRoles()方法。
    sslorg.apache.shiro.web.filter.authz.SslFilter/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
    userorg.apache.shiro.web.filter.authc.UserFilter/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
    logoutorg.apache.shiro.web.filter.authc.LogoutFilter
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值