nbsaas-boot适配Shiro Cache的实现

在构建基于nbsaas-boot的应用时,Shiro作为一个强大的安全框架,被广泛应用于用户身份认证和权限控制。在实际应用中,为了提高性能并减轻数据库负担,我们通常会使用缓存来存储用户的认证信息和权限信息。本文将介绍如何通过nbsaas-boot适配Shiro Cache,使用Redis和Caffeine作为缓存实现。

Redis缓存实现

首先,我们定义了一个RedisCache类实现Shiro的Cache接口,用于操作Redis缓存。以下是代码实现:

public class RedisCache<K, V> implements Cache<K, V> {

    private static final String REDIS_SHIRO_CACHE = "nbsaas-cache:";
    private String cacheKey;
    private RedisTemplate<K, V> redisTemplate;
    private final long globExpire = 30;

    @SuppressWarnings("rawtypes")
    public RedisCache(String name, RedisTemplate client) {
        this.cacheKey = REDIS_SHIRO_CACHE + name + ":";
        this.redisTemplate = client;
    }

    @Override
    public V get(K key) throws CacheException {
        //设置key值过期时间,过期后将删除key值
//        redisTemplate.boundValueOps(getCacheKey(key)).expire(globExpire, TimeUnit.MINUTES);
        //设置key值永不过期
        redisTemplate.boundValueOps(getCacheKey(key));
        return redisTemplate.boundValueOps(getCacheKey(key)).get();
    }

    @Override
    public V put(K key, V value) throws CacheException {
        V old = get(key);
        redisTemplate.boundValueOps(getCacheKey(key)).set(value);
        return old;
    }

    @Override
    public V remove(K key) throws CacheException {
        V old = get(key);
        redisTemplate.delete(getCacheKey(key));
        return old;
    }

    @Override
    public void clear() throws CacheException {
        redisTemplate.delete(keys());
    }

    @Override
    public int size() {
        return keys().size();
    }

    @Override
    public Set<K> keys() {
        return redisTemplate.keys(getCacheKey("*"));
    }

    @Override
    public Collection<V> values() {
        Set<K> set = keys();
        List<V> list = new ArrayList<>();
        for (K s : set) {
            list.add(get(s));
        }
        return list;
    }

    private K getCacheKey(Object k) {
        return (K) (this.cacheKey + k);
    }
}

然后,我们创建了一个RedisCacheManager类,实现Shiro的CacheManager接口,用于管理Redis缓存的创建和获取:

public class RedisCacheManager implements CacheManager {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public <K, V> Cache<K, V> getCache(String name) throws CacheException {
        return new RedisCache<K, V>(name, redisTemplate);
    }

    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

}

Caffeine缓存实现

接下来,我们定义了一个基于Caffeine的ShiroCaffeineCache类实现Shiro的Cache接口,用于操作本地缓存:

public class ShiroCaffeineCache<K, V> implements Cache<K, V> {

    LoadingCache<K, V> cache;

    public ShiroCaffeineCache(LoadingCache<K, V> cache) {
        this.cache = cache;
    }

    /**
     *
     * @param init 初始容量
     * @param max    最大容量
     */
    public ShiroCaffeineCache(int init,int max) {
        cache= Caffeine.newBuilder()
                .initialCapacity(init)
                .maximumSize(max)
                .build(key -> null);
    }
    /**
     *
     * @param init 初始容量
     * @param max    最大容量
     * @param expireAfterWrite 数据写入以后多久过期时间
     * @param expireAfterAccess 数据访问以后多久过期时间
     */
    public ShiroCaffeineCache(int init,int max,int expireAfterWrite,int expireAfterAccess) {
        cache= Caffeine.newBuilder()
                .initialCapacity(init)
                .maximumSize(max)
                .expireAfterWrite(expireAfterWrite, TimeUnit.SECONDS)
                .expireAfterAccess(expireAfterAccess,TimeUnit.SECONDS)
                .build(key -> null);
    }
    @Override
    public V get(K key) throws CacheException {
        return cache.getIfPresent(key);
    }

    @Override
    public V put(K key, V value) throws CacheException {
        cache.put(key, value);
        return value;
    }

    @Override
    public V remove(K key) throws CacheException {
        V value = cache.getIfPresent(key);
        cache.invalidate(key);
        return value;
    }

    @Override
    public void clear() throws CacheException {
        cache.invalidateAll();
    }

    @Override
    public int size() {
        return (int) cache.estimatedSize();
    }

    @Override
    public Set<K> keys() {
        return cache.asMap().keySet();
    }

    @Override
    public Collection<V> values() {
        return cache.asMap().values();
    }


}

最后,我们创建了一个ShiroCaffeineCacheManager类,继承自Shiro的AbstractCacheManager,用于管理Caffeine缓存的创建和获取:

public class ShiroCaffeineCacheManager extends AbstractCacheManager {

    /**
     * 初始容量
     */
    int init;

    /**
     * 最大容量
     */
    int max;

    /**
     * 数据写入以后多久过期时间
     */
    int expireAfterWrite;

    /**
     * 数据访问以后多久过期时间
     */
    int expireAfterAccess;

    /**
     *
     * @param init 初始容量
     * @param max    最大容量
     * @param expireAfterWrite 数据写入以后多久过期时间
     * @param expireAfterAccess 数据访问以后多久过期时间
     */
    public ShiroCaffeineCacheManager(int init, int max, int expireAfterWrite, int expireAfterAccess) {
        this.init = init;
        this.max = max;
        this.expireAfterWrite = expireAfterWrite;
        this.expireAfterAccess = expireAfterAccess;
    }



    public ShiroCaffeineCacheManager() {
        this.init = 100;
        this.max = 5000;
        this.expireAfterWrite = 60*60;
        this.expireAfterAccess = 60*30;
    }


    @Override
    protected Cache createCache(String name) throws CacheException {
        return new ShiroCaffeineCache(init,max,expireAfterWrite,expireAfterAccess);
    }
}

nbsaas-boot适配

为了使上述缓存实现与nbsaas-boot无缝集成,你需要在配置文件中配置相应的Bean。在Spring Boot应用中,可以通过@Configuration注解的类进行配置。

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(shiroRealm());
        securityManager.setSessionManager(sessionManager());
        securityManager.setCacheManager(new ShiroCaffeineCacheManager());
        return securityManager;
    }

    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        return bean;
    }

通过上述配置,你就成功地将nbsaas-boot与Shiro缓存进行了集成。在实际应用中,可以根据具体需求进行调整和优化,以满足不同场景的性能和安全需求。希望本文对你在nbsaas-boot项目中使用Shiro缓存提供了一些帮助。