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

来源:互联网 发布:辐射4低配优化补丁 编辑:程序博客网 时间:2024/06/03 14:53

简述

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

Shiro提供的接口

  1. Shiro提供的Cache接口:
public interface Cache<K, V> {      //根据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<K> keys();      //获取缓存中所有的value      public Collection<V> values();  }
  1. Shiro提供的CacheManager接口
public interface CacheManager {      //根据缓存名字获取一个Cache      public <K, V> Cache<K, V> 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中配置缓存管理器。

    <!--1. 安全管理器-->    <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>    <!--2. 自定义Realm-->    <bean id="shiroRealm" class="com.why.authority.realms.ShiroRealm">        <!-- 依赖凭证匹配器 -->        <property name="credentialsMatcher" ref="credentialsMatcher"/>        <!--启用缓存,默认关闭-->        <property name="cachingEnabled" value="true"/>        <!--启用身份验证缓存,即缓存AuthenticationInfo,默认false-->        <property name="authenticationCachingEnabled" value="true"/>        <!--启用授权缓存,即缓存AuthorizationInfo的信息,默认为false-->        <property name="authorizationCachingEnabled" value="true"/>    </bean>    <!--6. 会话管理器-->    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">        <!--删除在session过期时跳转页面时自动在URL中添加JSESSIONID-->        <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>    <!-- 7. 缓存管理器-->    <!-- 7.1 这里配置我们自定义的缓存管理器-->    <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<byte[]> keys = JedisClientSingle.getJedis().keys(new String("*").getBytes());        Set<Object> set = new HashSet<Object>();        for (byte[] bs : keys) {            set.add(ByteSourceUtils.deserialize(bs));        }        return set;    }    /**     * 获取所有的value     */    @Override    public Collection values() {        Set keys = this.keys();        List<Object> values = new ArrayList<Object>();        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 <K, V> Cache<K, V> getCache(String name) throws CacheException {        return new RedisCache<K,V>();    }}

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<UserEntity> 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,但是通过不断的调试和查资料,还是都解决了,看到功能成功实现,还是很开心的。

原创粉丝点击