[Shiro入门](一)使用Redis作为缓存管理器

简述

Shiro提供了类似Spring的Cache抽象,即Shiro本身不实现Cache,但是对Cache进行了又抽象,方便更换不同的底层Cache实现,而Shiro官方提供了对于Ehcache缓存的支持,并提供了shiro-ehcache.jar的包。但是我们想要用Redis来作为缓存管理器,Ehcache和Redis的优劣可以去自行百度。后面我也会通过查资料总结一下的。

Shiro提供的接口

  1. Shiro提供的Cache接口:
public interface Cache {  
    //根据Key获取缓存中的值  
    public V get(K key) throws CacheException;  
    //往缓存中放入key-value,返回缓存中之前的值  
    public V put(K key, V value) throws CacheException;   
    //移除缓存中key对应的值,返回该值  
    public V remove(K key) throws CacheException;  
    //清空整个缓存  
    public void clear() throws CacheException;  
    //返回缓存大小  
    public int size();  
    //获取缓存中所有的key  
    public Set keys();  
    //获取缓存中所有的value  
    public Collection values();  
}
  1. Shiro提供的CacheManager接口
public interface CacheManager {  
    //根据缓存名字获取一个Cache  
    public  Cache getCache(String name) throws CacheException;  
}  
  1. Shiro提供了CacheManagerAware用户注入CacheManager
public interface CacheManagerAware {  
    //注入CacheManager  
    void setCacheManager(CacheManager cacheManager);  
}  

Shiro内部响应的组件(DefaultSecurityManager)会自动检测响应的对象(如Realm)是否实现了CacheManagerAware并自动注入响应的CacheManager。

配置缓存管理器

我们要使用Redis作为缓存管理器,那么我们就需要实现这些接口,但是前提是我们要在Spring-Shiro.xml中配置缓存管理器。

    
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="sessionManager" ref="sessionManager"/>
        <property name="cacheManager" ref="customCacheManager"/>
        <property name="realms">
            <list>
                <ref bean="shiroRealm"/>
            list>
        property>
    bean>

    
    <bean id="shiroRealm" class="com.why.authority.realms.ShiroRealm">
        
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
        
        <property name="cachingEnabled" value="true"/>
        
        <property name="authenticationCachingEnabled" value="true"/>
        
        <property name="authorizationCachingEnabled" value="true"/>
    bean>

    
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        
        <property name="sessionIdUrlRewritingEnabled" value="false" />
        
        <property name="globalSessionTimeout" value="1800000"/>
        <property name="deleteInvalidSessions" value="true"/>
        <property name="sessionValidationSchedulerEnabled" value="true"/>
        <property name="sessionIdCookieEnabled" value="true"/>
    bean>

    
    
    <bean id="customCacheManager" class="com.why.authority.cache.CustomCacheManager"/>

创建RedisCache实现Cache接口

package com.why.authority.cache;

import com.why.utils.jedis.JedisClientSingle;
import com.why.utils.serializable.ByteSourceUtils;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import redis.clients.jedis.Jedis;

import java.io.Serializable;
import java.util.*;

/**
 * @author: 王洪玉
 * @decsription: 缓存管理器
 * @create: 2017/12/5 21:18
 * @modified By:
 */
public class RedisCache<K,V> implements Cache<K,V> {

    public String getKeyPrefix() {
        return keyPrefix;
    }

    public void setKeyPrefix(String keyPrefix) {
        this.keyPrefix = keyPrefix;
    }

    private String keyPrefix = "shiro_redis_session:";
    /**
     * 获得byte[]型的key
     * @param key
     * @return
     */
    private byte[] getByteKey(Object key){
        if(key instanceof String){
            String preKey = this.keyPrefix + key;
            return preKey.getBytes();
        }else{
            return ByteSourceUtils.serialize((Serializable) key);
        }
    }


    @Override
    public Object get(Object key) throws CacheException {

        byte[] bytes = getByteKey(key);
        byte[] value = JedisClientSingle.getJedis().get(bytes);
        if(value == null){
            return null;
        }
        return ByteSourceUtils.deserialize(value);
    }

    /**
     * 将shiro的缓存保存到redis中
     */
    @Override
    public Object put(Object key, Object value) throws CacheException {

        Jedis jedis = JedisClientSingle.getJedis();

        jedis.set(getByteKey(key), ByteSourceUtils.serialize((Serializable)value));
        byte[] bytes = jedis.get(getByteKey(key));
        Object object = ByteSourceUtils.deserialize(bytes);

        return object;

    }

    @Override
    public Object remove(Object key) throws CacheException {
        Jedis jedis = JedisClientSingle.getJedis();

        byte[] bytes = jedis.get(getByteKey(key));

        jedis.del(getByteKey(key));

        return ByteSourceUtils.deserialize(bytes);
    }

    /**
     * 清空所有缓存
     */
    @Override
    public void clear() throws CacheException {
        JedisClientSingle.getJedis().flushDB();
    }

    /**
     * 缓存的个数
     */
    @Override
    public int size() {
        Long size = JedisClientSingle.getJedis().dbSize();
        return size.intValue();
    }

    /**
     * 获取所有的key
     */
    @Override
    public Set keys() {
        Set keys = JedisClientSingle.getJedis().keys(new String("*").getBytes());
        Set set = new HashSet();
        for (byte[] bs : keys) {
            set.add(ByteSourceUtils.deserialize(bs));
        }
        return set;
    }


    /**
     * 获取所有的value
     */
    @Override
    public Collection values() {
        Set keys = this.keys();

        List values = new ArrayList();
        for (Object key : keys) {
            byte[] bytes = JedisClientSingle.getJedis().get(getByteKey(key));
            values.add(ByteSourceUtils.deserialize(bytes));
        }
        return values;
    }
}
 
  

创建CustomCacheManager实现CacheManager接口

package com.why.authority.cache;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;

/**
 * @author: 王洪玉
 * @decsription: 自定义缓存管理器
 * @create: 2017/12/5 21:43
 * @modified By:
 *
 */
public class CustomCacheManager implements CacheManager {

    @Override
    public  Cache getCache(String name) throws CacheException {
        return new RedisCache();
    }
}

Jedis工具类JedisClientSingle

package com.why.utils.jedis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * 单机版
 * @author why
 * @Time 2017年10月31日
 */
public class JedisClientSingle {

    private static JedisPool jedisPool;

    static {
        JedisPoolConfig jedisConfig = new JedisPoolConfig();
        jedisConfig.setMaxTotal(100);
        jedisConfig.setMaxIdle(10);
        jedisConfig.setMaxWaitMillis(100);
        //主机名称和端口号,开启redis的服务器和端口号
        jedisPool = new JedisPool(jedisConfig, "192.168.131.128", 6379);
    }

    public static Jedis getJedis() {
        return jedisPool.getResource();
    }

    public static void close(Jedis jedis) {
        jedis.close();
    }


}

登录认证

由于在Spring-Shiro.xml文件中已经配置开启登录时开启缓存,那么当用户登录的时候,会默认先调用RedisCache的get方法从缓存中获取,如果没有该用户的缓存的话,则执行登录的业务查询数据库,然后将查询出来的数据存入到缓存中。

自定义Realm实现登录验证

@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        //1.把AuthenticationToken转换为UsernamePasswordToken
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;

        //2.从UsernamePasswordToken中获取userCode
        String userCode = usernamePasswordToken.getUsername();
        //3.获取用户信息userEntity
        List userEntityList = userFacade.findByUserCode(userCode);
        UserEntity userEntity=userEntityList.get(0);

        //4.根据用户的情况,来构建AuthenticationInfo对象并返回
        String credentials = userEntity.getPassword();
        //使用ByteSource.Util.bytes()来计算盐值
        //ByteSource credentialsSalt = ByteSource.Util.bytes(userCode);

        return new SimpleAuthenticationInfo(userEntity, credentials,new MySimpleByteSource(userCode),getName());
    }

注意:这里最初用的是下面的代码,但是会报java.io.NotSerializableException:org.apache.shiro.util.SimpleByteSource异常,出现这个问题的原因就是在将用户信息存入Redis之前,需要将SimpleAuthenticationInfo信息进行序列化,但是SimpleByteSource没有是实现Serializable接口
解决办法:自定义一个类继承SimpleByteSource实现Serializable接口或实现ByteSource接口和Serializable接口,具体请看我的这篇博客:点击这里

ByteSource credentialsSalt = ByteSource.Util.bytes(userCode);
return new SimpleAuthenticationInfo(userEntity, credentials,credentialsSalt,getName());

总结:到此为止,我们的Shiro的缓存管理器就成功的切换成Redis了,过程中学习到了很多,遇到了各种BUG,但是通过不断的调试和查资料,还是都解决了,看到功能成功实现,还是很开心的。

你可能感兴趣的:(꧁项目实战꧂,♚✡✡✡✡✡✡♚【权限】)