Spring Boot Oauth2快取UserDetails到Ehcache

simonsundev發表於2019-01-19

在Spring中有一個類CachingUserDetailsService實現了UserDetailsService介面,該類使用靜態代理模式為UserDetailsService提供快取功能。該類原始碼如下:

CachingUserDetailsService.java

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

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

    public UserCache getUserCache() {
        return this.userCache;
    }

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

    public UserDetails loadUserByUsername(String username) {
        UserDetails user = this.userCache.getUserFromCache(username);
        if (user == null) {
            user = this.delegate.loadUserByUsername(username);
        }

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

CachingUserDetailsService預設的userCache屬性值為new NullUserCache(),該物件並未實現快取。因為我打算使用EhCache來快取UserDetails,所以需要使用Spring的EhCacheBasedUserCache類,該類是UserCache介面的實現類,主要是快取操作。

快取UserDetails到Ehcache的具體實現如下:

ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
    <!-- 磁碟快取位置 -->
    <diskStore path="java.io.tmpdir" />

    <cache name="userCache"
           maxElementsInMemory="0"
           eternal="true"
           overflowToDisk="true"
           diskPersistent="true"
           memoryStoreEvictionPolicy="LRU">
    </cache>
</ehcache>

UserDetailsCacheConfig.java

@Slf4j
@Configuration
public class UserDetailsCacheConfig {
    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Bean
    public UserCache userCache(){
        try {
            EhCacheBasedUserCache userCache = new EhCacheBasedUserCache();
            val cacheManager = CacheManager.getInstance();
            val cache = cacheManager.getCache("userCache");
            userCache.setCache(cache);
            return userCache;
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return null;
    }

    @Bean
    public UserDetailsService userDetailsService(){
        Constructor<CachingUserDetailsService> ctor = null;
        try {
            ctor = CachingUserDetailsService.class.getDeclaredConstructor(UserDetailsService.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        Assert.notNull(ctor, "CachingUserDetailsService constructor is null");
        ctor.setAccessible(true);

        CachingUserDetailsService cachingUserDetailsService = BeanUtils.instantiateClass(ctor, customUserDetailsService);
        cachingUserDetailsService.setUserCache(userCache());
        return cachingUserDetailsService;
    }
}

使用

@Autowired
private UserDetailsService userDetailsService;

歡迎關注我的oauthserver專案,僅僅需要執行建表sql,修改資料庫的連線配置,即可得到一個Spring Boot Oauth2 Server微服務。專案地址https://github.com/jeesun/oauthserver

相關文章