鍍金池/ 教程/ Java/ 緩存 UserDetails
初體驗
權(quán)限鑒定基礎(chǔ)
Remember-Me 功能
匿名認(rèn)證
intercept-url配置
認(rèn)證簡介
退出登錄 logout
AuthenticationProvider
Filter
關(guān)于登錄
異常信息本地化
緩存 UserDetails
session 管理
核心類簡介

緩存 UserDetails

Spring Security 提供了一個實現(xiàn)了可以緩存 UserDetails 的 UserDetailsService 實現(xiàn)類,CachingUserDetailsService。該類的構(gòu)造接收一個用于真正加載 UserDetails 的 UserDetailsService 實現(xiàn)類。當(dāng)需要加載 UserDetails 時,其首先會從緩存中獲取,如果緩存中沒有對應(yīng)的 UserDetails 存在,則使用持有的 UserDetailsService 實現(xiàn)類進(jìn)行加載,然后將加載后的結(jié)果存放在緩存中。UserDetails 與緩存的交互是通過 UserCache 接口來實現(xiàn)的。CachingUserDetailsService 默認(rèn)擁有 UserCache 的一個空實現(xiàn)引用,NullUserCache。以下是 CachingUserDetailsService 的類定義。

public class CachingUserDetailsService implements UserDetailsService {
    private UserCache userCache = new NullUserCache();
    private final UserDetailsService delegate;

    CachingUserDetailsService(UserDetailsService delegate) {
        this.delegate = delegate;
    }

    public UserCache getUserCache() {
        return userCache;
    }

    public void setUserCache(UserCache userCache) {
        this.userCache = userCache;
    }

    public UserDetails loadUserByUsername(String username) {
        UserDetails user = userCache.getUserFromCache(username);

        if (user == null) {
            user = delegate.loadUserByUsername(username);
        }

        Assert.notNull(user, "UserDetailsService" + delegate + "returned null for username" + username + "." +
                "This is an interface contract violation");

        userCache.putUserInCache(user);

        return user;
    }
}

我們可以看到當(dāng)緩存中不存在對應(yīng)的 UserDetails 時將使用引用的 UserDetailsService 類型的 delegate 進(jìn)行加載。加載后再把它存放到 Cache 中并進(jìn)行返回。除了 NullUserCache 之外,Spring Security 還為我們提供了一個基于 Ehcache 的 UserCache 實現(xiàn)類,EhCacheBasedUserCache,其源碼如下所示。

public class EhCacheBasedUserCache implements UserCache, InitializingBean {

    private static final Log logger = LogFactory.getLog(EhCacheBasedUserCache.class);

    private Ehcache cache;

    public void afterPropertiesSet() throws Exception {
        Assert.notNull(cache, "cache mandatory");
    }

    public Ehcache getCache() {
        returncache;
    }

    public UserDetails getUserFromCache(String username) {
        Element element = cache.get(username);
        if (logger.isDebugEnabled()) {
            logger.debug("Cache hit:" + (element != null) + "; username:" + username);
        }
        if (element == null) {
            returnnull;
        } else {
            return (UserDetails) element.getValue();
        }
    }

    public void putUserInCache(UserDetails user) {
        Element element = new Element(user.getUsername(), user);
        if (logger.isDebugEnabled()) {
            logger.debug("Cache put:" + element.getKey());
        }
        cache.put(element);
    }

    public void removeUserFromCache(UserDetails user) {
        if (logger.isDebugEnabled()) {
            logger.debug("Cache remove:" + user.getUsername());
        }
        this.removeUserFromCache(user.getUsername());
    }

    public void removeUserFromCache(String username) {
        cache.remove(username);
    }

    public void setCache(Ehcache cache) {
        this.cache = cache;
    }
}

從上述源碼我們可以看到 EhCacheBasedUserCache 所引用的 Ehcache 是空的,所以,當(dāng)我們需要對 UserDetails 進(jìn)行緩存時,我們只需要定義一個 Ehcache 實例,然后把它注入給 EhCacheBasedUserCache 就可以了。接下來我們來看一下定義一個支持緩存 UserDetails 的 CachingUserDetailsService 的示例。

   <security:authentication-manager alias="authenticationManager">
      <!-- 使用可以緩存 UserDetails 的 CachingUserDetailsService -->
      <security:authentication-provider
         user-service-ref="cachingUserDetailsService" />
   </security:authentication-manager>
   <!-- 可以緩存 UserDetails 的 UserDetailsService -->
   <bean id="cachingUserDetailsService" class="org.springframework.security.config.authentication.CachingUserDetailsService">
      <!-- 真正加載 UserDetails 的 UserDetailsService -->
      <constructor-arg ref="userDetailsService"/>
      <!-- 緩存 UserDetails 的 UserCache -->
      <property name="userCache">
         <bean class="org.springframework.security.core.userdetails.cache.EhCacheBasedUserCache">
            <!-- 用于真正緩存的 Ehcache 對象 -->
            <property name="cache" ref="ehcache4UserDetails"/>
         </bean>
      </property>
   </bean>
   <!-- 將使用默認(rèn)的 CacheManager 創(chuàng)建一個名為 ehcache4UserDetails 的 Ehcache 對象 -->
   <bean id="ehcache4UserDetails" class="org.springframework.cache.ehcache.EhCacheFactoryBean"/>
   <!-- 從數(shù)據(jù)庫加載 UserDetails 的 UserDetailsService -->
   <bean id="userDetailsService"
      class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
      <property name="dataSource" ref="dataSource" />
   </bean>

在上面的配置中,我們通過 EhcacheFactoryBean 定義的 Ehcache bean 對象采用的是默認(rèn)配置,其將使用默認(rèn)的 CacheManager,即直接通過 CacheManager.getInstance() 獲取當(dāng)前已經(jīng)存在的 CacheManager 對象,如不存在則使用默認(rèn)配置自動創(chuàng)建一個,當(dāng)然這可以通過 cacheManager 屬性指定我們需要使用的 CacheManager,CacheManager 可以通過 EhCacheManagerFactoryBean 進(jìn)行定義。此外,如果沒有指定對應(yīng)緩存的名稱,默認(rèn)將使用 beanName,在上述配置中即為 ehcache4UserDetails,可以通過 cacheName 屬性進(jìn)行指定。此外,緩存的配置信息也都是使用的默認(rèn)的。更多關(guān)于 Spring 使用 Ehcache 的信息可以參考我的另一篇文章《Spring 使用 Cache》。

上一篇:初體驗下一篇:Filter