spring-shiro.xml文件配置
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="customAuthorizingRealm"/> <property name="cacheManager" ref="ehCacheManager"/> </bean> <bean id="ehCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:shiro/ehcache.xml"/> <bean/> <bean id="customAuthorizingRealm" class="com.wjz.demo.CustomAuthorizingRealm"> <property name="authenticationCachingEnabled" value="true"/> <property name="authorizationCachingEnabled" value="true"/> </bean>
緩存原理
CachingSecurityManager
注入緩存管理器到安全管理器中,還需要將緩存管理器注入到領域中
public void setCacheManager(CacheManager cacheManager) { this.cacheManager = cacheManager; afterCacheManagerSet(); }
RealmSecurityManager
protected void afterCacheManagerSet() { super.afterCacheManagerSet(); applyCacheManagerToRealms(); }
將緩存管理器注入到領域中,存入取出緩存內容均有領域具體實現
protected void applyCacheManagerToRealms() {
// 獲得由配置部分注入的EhCacheManager CacheManager cacheManager = getCacheManager(); Collection<Realm> realms = getRealms(); if (cacheManager != null && realms != null && !realms.isEmpty()) { for (Realm realm : realms) { if (realm instanceof CacheManagerAware) {
// 將EhCacheManage注入到Realm中 ((CacheManagerAware) realm).setCacheManager(cacheManager); } } } }
DefaultSecurityManager開始認證
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException { AuthenticationInfo info; try { info = authenticate(token); } catch (AuthenticationException ae) { try { onFailedLogin(token, ae, subject); } catch (Exception e) { if (log.isInfoEnabled()) { log.info("onFailedLogin method threw an " + "exception. Logging and propagating original AuthenticationException.", e); } } throw ae; //propagate } Subject loggedIn = createSubject(token, info, subject); onSuccessfulLogin(token, info, loggedIn); return loggedIn; }
public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException { return this.authenticator.authenticate(token); }
獲得我們自定義的Realm領域如CustomAuthorizingRealm
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { assertRealmsConfigured(); Collection<Realm> realms = getRealms(); if (realms.size() == 1) { return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken); } else { return doMultiRealmAuthentication(realms, authenticationToken); } }
從緩存中找認證信息,如果沒有找到則執行CustomAuthorizingRealm中的doGetAuthenticationInfo方法獲得AuthenticationInfo
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 試圖從緩存中獲得AuthenticationInfo,后文詳解#1 AuthenticationInfo info = getCachedAuthenticationInfo(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) {
// 將獲得的AuthenticationInfo放入緩存,后文詳解#2 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; }
書接前文#1
試圖從緩存中獲得AuthenticationInfo
private AuthenticationInfo getCachedAuthenticationInfo(AuthenticationToken token) { AuthenticationInfo info = null; // 獲得可用的緩存,后文詳解#1-1 Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache(); if (cache != null && token != null) { log.trace("Attempting to retrieve the AuthenticationInfo from cache.");
// 以用戶名為key Object key = getAuthenticationCacheKey(token);
// 根據key從緩存中拿到AuthenticationInfo,后文詳解#1-2 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; }
書接前文#1-1
AuthenticatingRealm中有一個Cache<Object, AuthenticationInfo> authenticationCache屬性
private Cache<Object, AuthenticationInfo> getAvailableAuthenticationCache() {
// 獲得authenticationCache Cache<Object, AuthenticationInfo> cache = getAuthenticationCache();
// 默認為false,需要set注入為true,參考配置部分 boolean authcCachingEnabled = isAuthenticationCachingEnabled(); if (cache == null && authcCachingEnabled) {
// 使用CacheManager獲得Cache對象 cache = getAuthenticationCacheLazy(); } return cache; }
獲得EhCacheManager並使用它獲得緩存
private Cache<Object, AuthenticationInfo> getAuthenticationCacheLazy() { if (this.authenticationCache == null) { log.trace("No authenticationCache instance set. Checking for a cacheManager..."); // 獲得EhCacheManager CacheManager cacheManager = getCacheManager(); if (cacheManager != null) {
// 獲得緩存的name String cacheName = getAuthenticationCacheName(); log.debug("CacheManager [{}] configured. Building authentication cache '{}'", cacheManager, cacheName);
// 根據緩存的name獲得緩存 this.authenticationCache = cacheManager.getCache(cacheName); } } return this.authenticationCache; }
EhCacheManager
public final <K, V> Cache<K, V> getCache(String name) throws CacheException { if (log.isTraceEnabled()) { log.trace("Acquiring EhCache instance named [" + name + "]"); } try {
// ensureCacheManager()獲得net.sf.ehcache.CacheManager net.sf.ehcache.Ehcache cache = ensureCacheManager().getEhcache(name); if (cache == null) { if (log.isInfoEnabled()) { log.info("Cache with name '{}' does not yet exist. Creating now.", name); }
// 調用net.sf.ehcache的API this.manager.addCache(name); cache = manager.getCache(name); if (log.isInfoEnabled()) { log.info("Added EhCache named [" + name + "]"); } } else { if (log.isInfoEnabled()) { log.info("Using existing EHCache named [" + cache.getName() + "]"); } }
// 封裝net.sf.ehcache為shiro的org.apache.shiro.cache.ehcache.EhCache return new EhCache<K, V>(cache); } catch (net.sf.ehcache.CacheException e) { throw new CacheException(e); } }
net.sf.ehcache.Ehcache
private net.sf.ehcache.Ehcache cache; /** * Constructs a new EhCache instance with the given cache. * * @param cache - delegate EhCache instance this Shiro cache instance will wrap. */ public EhCache(net.sf.ehcache.Ehcache cache) { if (cache == null) { throw new IllegalArgumentException("Cache argument cannot be null."); } this.cache = cache; }
書接前文#1-2
shiro的EhCache內部裝備一個net.sf.ehcache.Ehcache
public V get(K key) throws CacheException { try { if (log.isTraceEnabled()) { log.trace("Getting object from cache [" + cache.getName() + "] for key [" + key + "]"); } if (key == null) { return null; } else {
// 使用net.sf.ehcache.Ehcache的API獲得Element Element element = cache.get(key); if (element == null) { if (log.isTraceEnabled()) { log.trace("Element for [" + key + "] is null."); } return null; } else { // 獲得緩存中的內容 return (V) element.getObjectValue(); } } } catch (Throwable t) { throw new CacheException(t); } }
書接前文#2
將獲得的AuthenticationInfo放入緩存
private void cacheAuthenticationInfoIfPossible(AuthenticationToken token, AuthenticationInfo info) {
// 判斷是否開啟了緩存功能及緩存授權功能 if (!isAuthenticationCachingEnabled(token, info)) { log.debug("AuthenticationInfo caching is disabled for info [{}]. Submitted token: [{}].", info, token); //return quietly, caching is disabled for this token/info pair: return; } Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache(); if (cache != null) { Object key = getAuthenticationCacheKey(token); cache.put(key, info); log.trace("Cached AuthenticationInfo for continued authentication. key=[{}], value=[{}].", key, info); } }
authenticationCachingEnabled的值默認為false需要set注入為true,cachingEnabled默認為true
public boolean isAuthenticationCachingEnabled() { return this.authenticationCachingEnabled && isCachingEnabled(); }
放入緩存
public V put(K key, V value) throws CacheException { if (log.isTraceEnabled()) { log.trace("Putting object in cache [" + cache.getName() + "] for key [" + key + "]"); } try { V previous = get(key); Element element = new Element(key, value); cache.put(element); return previous; } catch (Throwable t) { throw new CacheException(t); } }
授權與認證類似
