Shiro Realm设计概念
Realm
shiro最干实事的,其行为有
CachingRealm
其行为主要是注入缓存管理器、控制缓存开关、提供关于缓存的公共行为,他不提供缓存具体的认证和授权信息,该部分由其子类具体缓存
AuthenticatingRealm
具备缓存从数据库查出的用户信息(Cache<Object, AuthenticationInfo>),其行为主要是执行认证的具备行为,其具备CredentialsMatcher凭证匹配器,主要用于匹配用户输入的Token和数据库查出的Info是否匹配。该部分的用户信息还会注入到Subject中,为校验用户权限使用,但是就有了一个问题,用户的权限更新后怎么办,Shiro使用了runAs方式。
用户的权限信息更新后需要更新Subject中获取PrincipalCollection的方式,即runAs()方式,还需更新认证、授权缓存信息
package com.wjz.demo; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.SimplePrincipalCollection; import org.apache.shiro.subject.Subject; public class CustomAuthorizingRealm extends AuthorizingRealm { @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { return new SimpleAuthenticationInfo("wjz", "123", getName()); } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.addRole("buyGoods"); return authorizationInfo; } /** * 用户的权限信息被更新后须执行该方法 * * @param latestPrincipal * 最新的用户权限信息 */ public void reloadAuthorizing(Object latestPrincipal) { Subject subject = SecurityUtils.getSubject(); PrincipalCollection expiredPrincipalCollection = subject.getPrincipals(); String realmName = getName(); final SimplePrincipalCollection latestPrincipalCollection = new SimplePrincipalCollection(latestPrincipal, realmName); subject.releaseRunAs(); subject.runAs(latestPrincipalCollection); // 如果认证、授权还有缓存功能的话执行以下内容 if (isCachingEnabled()) { clearCache(expiredPrincipalCollection); CacheManager cacheManager = getCacheManager(); if (isAuthenticationCachingEnabled()) { Cache<Object, AuthenticationInfo> authenticationCache = cacheManager .getCache(getAuthenticationCacheName()); setAuthenticationCache(authenticationCache); } if (isAuthorizationCachingEnabled()) { Cache<Object, AuthorizationInfo> authorizationCache = cacheManager .getCache(getAuthorizationCacheName()); setAuthorizationCache(authorizationCache); } } } }
核心逻辑方法
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 试图从缓存中获得,没有缓存时使用CacheManager创建 AuthenticationInfo info = getCachedAuthenticationInfo(token); if (info == null) { // 子类具体实现返回具有从数据库查出的用户信息的AuthenticationInfo info = doGetAuthenticationInfo(token); log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info); if (token != null && info != null) { // 开启了缓存的话将AuthenticationInfo放到缓存中,Token的principal为key(UsernamePasswordToken的principal即username) cacheAuthenticationInfoIfPossible(token, info); } } else { log.debug("Using cached authentication info [{}] to perform credentials matching.", info); } if (info != null) { // Token和Info进行匹配 assertCredentialsMatch(token, info); } else { log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token); } return info; }
AuthorizingRealm
具备了权限解析器(PermissionResolver)和从数据库查出的用户权限缓存(Cache<Object, AuthorizationInfo>),其行为主要是校验用户的权限
核心逻辑方法根据Subject中的PrincipalCollection获得会员的权限信息
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) { // 子类具体实现返回具有从数据库查出的用户权限信息的AuthorizationInfo 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 + "]."); } // 开启了缓存的话将AuthorizationInfo放到缓存中,PrincipalCollection为key Object key = getAuthorizationCacheKey(principals); cache.put(key, info); } } return info; }
校验前期准备
public boolean isPermitted(PrincipalCollection principals, String permission) { // 使用权限解析器解析字符串获得用户的权限信息,实例化 WildcardPermission 并注入权限字符串 Permission p = getPermissionResolver().resolvePermission(permission); return isPermitted(principals, p); } public boolean isPermitted(PrincipalCollection principals, Permission permission) { // 调用核心逻辑方法获得用户的权限信息 AuthorizationInfo info = getAuthorizationInfo(principals); return isPermitted(permission, info); }
具体的校验
protected Collection<Permission> getPermissions(AuthorizationInfo info) { Set<Permission> permissions = new HashSet<Permission>(); // 当子类查询数据库查出用户权限信息后拼装AuthorizationInfo时可以注入字符串权限 // 也可以注入对象权限即 WildcardPermission if (info != null) { Collection<Permission> perms = info.getObjectPermissions(); if (!CollectionUtils.isEmpty(perms)) { permissions.addAll(perms); } // 是字符串权限的话还得解析为对象权限 WildcardPermission 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); } } //visibility changed from private to protected per SHIRO-332 protected boolean isPermitted(Permission permission, AuthorizationInfo info) { Collection<Permission> perms = getPermissions(info); if (perms != null && !perms.isEmpty()) { for (Permission perm : perms) { // 权限匹配 if (perm.implies(permission)) { return true; } } } return false; }