Shiro(3) 缓存机制

Shiro提供了缓存功能,以确保安全操作保持尽可能的快。
但是Shiro作为一个安全框架,不可能实现一个完整的缓存机制,因此,Shiro提供了一个抽象的缓存API,我们可以通过此套API来继承任何的缓存产品。
首先我们来看看Shiro的缓存API的接口:

public interface Cache<K,V> {

    public V get(K key) throws CacheException;//获取指定的键值(或null)对应的缓存实例,访问底仓缓存系统异常时抛出CacheException
    
    public V put(K key, V value) throws CacheException;//添加缓存实例

    public V remove(K key) throws CacheException;//删除指定的键值对应的缓存实例

    public void clear() throws CacheException;//清空全部缓存

    public int size();//获取缓存的数量
    
    public Set<K> keys();//返回所有缓存的键值集合
    
    public Collection<V> values();//获取所有缓存实例的集合
}
/**
 * 缓存管理器
 */
public interface CacheManager {

	// 根据指定的缓存名称获取对应的缓存实例。如果缓存实例不存在,则新建一个。
	<K, V> Cache<K,V> getCache(String name) throws CacheException;
}
public interface CacheManagerAware {

	//注入可用的CacheManager实例
    void setCacheManager(CacheManager cacheManager);
}

Shiro通过以上三个接口,使得扩展缓存机制变得非常容易,只要实现Cache,用来从缓存中增删改查数据,
然后实现CacheManager 来创建Cache对象就可以使用。

除了定义了以上三个接口,Shiro-core还提供了基于软引用来使JVM自动回收的缓存机制

public class MemoryConstrainedCacheManager extends AbstractCacheManager {

	@Override
	protected Cache createCache(String name) {
		return new MapCache<Object,Object>(name, new SoftHashMap<Object, Object>());
	}

}

SoftHashMap使用软引用来存放Map的 value,并在每次调用时清空 已经被垃圾收集器回收的Value对应的key。
接下来我们写个测试案例:

public class CacheService implements CacheManagerAware {
	
	private final String CACHE_NAME = getClass().getName()+".CACHE_NAME";
	private CacheManager cacheManager;
	
	private Datebase datebase = new Datebase();
	
	public CacheService() {
		super();
		this.cacheManager = new MemoryConstrainedCacheManager();
	}


	@Override
	public void setCacheManager(CacheManager cacheManager) {
		this.cacheManager = cacheManager;
	}
	
	public Object getValue(String key){
		Object value = null;
		//先从缓存中获取
		if(cacheManager != null){
			value = cacheManager.getCache(CACHE_NAME).get(key);
		}
		//没有则从Datebase中获取
		if(value == null){
			value = datebase.getValue(key);
			cacheManager.getCache(CACHE_NAME).put(key, value);
		}
		return value;
	}
}
class Datebase{
	
	private Map<String , Object> resMap = new HashMap<>();
	
	public Datebase(){
		resMap.put("1", "admin");
		resMap.put("2", "张三");
		resMap.put("3", Math.PI);
		resMap.put("5", resMap);
	}
	
	public Object getValue(String key){
		System.out.println("从 Datebase中获取Value:"+key);
		return resMap.get(key);
	}
}
public static void main(String[] args) {
	CacheService cacheService = new CacheService();
	System.out.println(cacheService.getValue("3"));
	System.out.println(cacheService.getValue("5"));
	System.out.println(cacheService.getValue("3"));
	System.out.println(cacheService.getValue("1"));
}

输出结果:

从 Datebase中获取Value:3
3.141592653589793
从 Datebase中获取Value:5
{1=admin, 2=张三, 3=3.141592653589793, 5=(this Map)}
3.141592653589793
从 Datebase中获取Value:1
admin

可以看到再次查询 键值为 3 的数据时,没有直接访问Datebase.

除了以上基于软引用和内存回收机制的缓存,Shiro还为我们提供了基于EhCache的缓存扩展。只需要我们引入对应的包,并注入CacheManager就可以使用。

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-ehcache</artifactId>
  <version>1.3.2</version>
</dependency>

接下来我们看看Shiro中对缓存的支持。
查看实现了CacheManagerAware 的类与他们的继承关系,可以看到支持缓存的组件有Realm, SecurityManager, SessionDAO, SessionManager。

先看看Realm对于身份认证的缓存:
CachingRealm

public abstract class CachingRealm implements Realm, Nameable, CacheManagerAware, LogoutAware {
    
    private boolean cachingEnabled; //是否开启缓存
    private CacheManager cacheManager; //缓存管理器
    
    // getter 、setter 、一些钩子方法
}

AuthenticatingRealm

public abstract class AuthenticatingRealm extends CachingRealm implements Initializable {
    //存放缓存数据的缓存实例
    private Cache<Object, AuthenticationInfo> authenticationCache;
    //是否开启身份认证缓存
    private boolean authenticationCachingEnabled;
    //缓存的名称,在CacheManager中的唯一key.
    private String authenticationCacheName;
    
    public AuthenticatingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
        //省略……
        //默认不开启缓存
        this.authenticationCachingEnabled = false;
        //省略……
        //缓存名称
        this.authenticationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
        //省略……
        if (cacheManager != null) {
            setCacheManager(cacheManager);
        }
        //……
    }
    
    public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //从缓存中获取 AuthenticationInfo , 可以理解为从 字段 authenticationCache中获取。
        AuthenticationInfo info = getCachedAuthenticationInfo(token);
        //获取不到,调用子类的 doGetAuthenticationInfo(token)实现。
        if (info == null) {
            //otherwise not cached, perform the lookup:
            info = doGetAuthenticationInfo(token);
            log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
            if (token != null && info != null) {
                cacheAuthenticationInfoIfPossible(token, info);
            }
        } else {
            log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
        }

        if (info != null) {
            assertCredentialsMatch(token, info);
        } else {
            log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
        }

        return info;
    }
    
    //从缓存中获取 AuthenticationInfo
    private AuthenticationInfo getCachedAuthenticationInfo(AuthenticationToken token) {
        AuthenticationInfo info = null;
        //获取缓存实例 Cache,可以认为是上面的字段 authenticationCache
        Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
        
        //从缓存中获取 AuthenticationInfo
        if (cache != null && token != null) {
            log.trace("Attempting to retrieve the AuthenticationInfo from cache.");
            Object key = getAuthenticationCacheKey(token);
            info = cache.get(key);
            if (info == null) {
                log.trace("No AuthorizationInfo found in cache for key [{}]", key);
            } else {
                log.trace("Found cached AuthorizationInfo for key [{}]", key);
            }
        }
        
        return info;
    }
    
    //获取缓存实例,可以认为是上面的字段 authenticationCache
    private Cache<Object, AuthenticationInfo> getAvailableAuthenticationCache() {
        Cache<Object, AuthenticationInfo> cache = getAuthenticationCache(); //字段中的authenticationCache
        // 判断缓存是否开启, 此处判断了 authenticationCachingEnabled 和从父类继承的cachingEnabled,都为true时,缓存开启
        boolean authcCachingEnabled = isAuthenticationCachingEnabled(); 
        if (cache == null && authcCachingEnabled) {
            // 如果缓存实例 为null, 从 CacheManager中获取
            cache = getAuthenticationCacheLazy();
        }
        return cache;
    }
    
}

以上代码显示了Realm 获取 AuthenticationInfo 回先尝试从CacheManager中获取, 获取不到才调用 doGetAuthenticationInfo(token)从数据源获取。

查看 DefaultSessionManager 的代码可以看到CacheManager主要用于设置 SessionDAO 的CacheManager,这里并未直接使用缓存。
接着查看SessionDAO, 查看源码可以看到在新增、修改Session时,会缓存Session, 删除时会清除缓存。读取Session会优先从缓存读取。

对于 SecurityManager,我们查看源码,可以得知主要是用于设置 Realm的 CacheManager.

public abstract class CachingSecurityManager implements SecurityManager, Destroyable, CacheManagerAware, EventBusAware {
    public void setCacheManager(CacheManager cacheManager) {
        this.cacheManager = cacheManager;
        afterCacheManagerSet();
    }
}

public abstract class RealmSecurityManager extends CachingSecurityManager {
    protected void afterCacheManagerSet() {
        super.afterCacheManagerSet();
        applyCacheManagerToRealms();
    }
    protected void applyCacheManagerToRealms() {
        CacheManager cacheManager = getCacheManager();
        Collection<Realm> realms = getRealms();
        if (cacheManager != null && realms != null && !realms.isEmpty()) {
            for (Realm realm : realms) {
                if (realm instanceof CacheManagerAware) {
                    ((CacheManagerAware) realm).setCacheManager(cacheManager);
                }
            }
        }
    }
}

public abstract class SessionsSecurityManager extends AuthorizingSecurityManager {
    
    protected void afterCacheManagerSet() {
        super.afterCacheManagerSet();
        applyCacheManagerToSessionManager();
    }
    
    protected void applyCacheManagerToSessionManager() {
        if (this.sessionManager instanceof CacheManagerAware) {
            ((CacheManagerAware) this.sessionManager).setCacheManager(getCacheManager());
        }
    }
}

因此,我们只需要设置SecurityManager的CacheManager,系统中的SessionManager 和 Realm都会使用此缓存机制。

在Realm中

public abstract class AuthenticatingRealm extends CachingRealm implements Initializable {
    
   public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //首先尝试从缓存中获取
        AuthenticationInfo info = getCachedAuthenticationInfo(token);
        
        //缓存中获取不到,再调用我们实现的从数据源中获取
        if (info == null) {
            info = doGetAuthenticationInfo(token);
            if (token != null && info != null) {
                cacheAuthenticationInfoIfPossible(token, info);
            }
        }
        //省略……
        return info;
    }
    
    private AuthenticationInfo getCachedAuthenticationInfo(AuthenticationToken token) {
        AuthenticationInfo info = null;
        //获取缓存实例,如果字段中的缓存实例为null,从cacheManager中获取
        Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
        //从缓存中获取 AuthenticationInfo
        if (cache != null && token != null) {
            Object key = getAuthenticationCacheKey(token);
            info = cache.get(key);
        }

        return info;
    }
}

public abstract class AuthorizingRealm extends AuthenticatingRealm
        implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware {
    
    protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
        //省略……
        //从缓存中获取权限信息
        Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
        if (cache != null) {
            Object key = getAuthorizationCacheKey(principals);
            info = cache.get(key);
        }

        //如果从缓存中获取不到,调用我们实现的 doGetAuthorizationInfo 获取,然后放入缓存。
        if (info == null) {
            info = doGetAuthorizationInfo(principals);
            if (info != null && cache != null) {
                Object key = getAuthorizationCacheKey(principals);
                cache.put(key, info);
            }
        }
        return info;
    }

SessionManager的缓存:

public class DefaultSessionManager extends AbstractValidatingSessionManager implements CacheManagerAware {
	@Override
    public void setCacheManager(CacheManager cacheManager) {
        this.cacheManager = cacheManager;
        //将sessionDAO 的缓存策略更新为cacheManager
        applyCacheManagerToSessionDAO();
    }

	//将sessionDAO 的缓存策略更新为cacheManager
    private void applyCacheManagerToSessionDAO() {
        if (this.cacheManager != null && this.sessionDAO != null && this.sessionDAO instanceof CacheManagerAware) {
            ((CacheManagerAware) this.sessionDAO).setCacheManager(this.cacheManager);
        }
    }
    
}

public abstract class CachingSessionDAO extends AbstractSessionDAO implements CacheManagerAware {
    //尝试从缓存中读取Session,读取不到调用父类的实现
    @Override
    public Session readSession(Serializable sessionId) throws UnknownSessionException {
        Session s = getCachedSession(sessionId);
        if (s == null) {
            s = super.readSession(sessionId);
        }
        return s;
    }
    
    //更新Session信息,并重新缓存Session
    @Override
    public void update(Session session) throws UnknownSessionException {
        doUpdate(session);
        if (session instanceof ValidatingSession) {
            if (((ValidatingSession) session).isValid()) {
                cache(session, session.getId());
            } else {
                uncache(session);
            }
        } else {
            cache(session, session.getId());
        }
    }
    
    //首先从缓存中删除,然后从EIS中删除
    @Override
    public void delete(Session session) {
        uncache(session);
        doDelete(session);
    }

    //调用 父类的实现,并缓存起来
    @Override
    public Serializable create(Session session) {
        Serializable sessionId = super.create(session);
        cache(session, sessionId);
        return sessionId;
    }
    
    //将Session放入缓存
    protected void cache(Session session, Serializable sessionId) {
        if (session == null || sessionId == null) {
            return;
        }
        Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
        if (cache == null) {
            return;
        }
        cache(session, sessionId, cache);
    }
    
    
    //从缓存中删除Session
    protected void uncache(Session session) {
        if (session == null) {
            return;
        }
        Serializable id = session.getId();
        if (id == null) {
            return;
        }
        Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
        if (cache != null) {
            cache.remove(id);
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值