Shiro功能應用(七)--Shiro集成Redis緩存(shiro-redis3.1.0)

     如果單機,使用EHCache就可以的,單如果多節點部署時就不行了,本文主要將Shiro和Redis緩存集成,在上一篇文章Shiro功能應用(六)–登陸失敗重試次數控制代碼基礎進行添加Redis緩存。

代碼實現:

      代碼地址:
          https://github.com/OooooOz/SpringBoot-Shiro

     首先將ShiroConfig關於EHCache的和SessionManager的配置去掉。

     ShiroConfig的安全管理器SecurityManager:

  @Bean(name="securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("shiroRealm") MyShiroRealm shiroRealm){
		... ... ... ...
		securityManager.setCacheManager(redisCacheManager());		    //配置 redis緩存管理器
        securityManager.setSessionManager(redisSessionManager());		//配置 redissession管理
        return securityManager;
    }

     ShiroConfig的Redis緩存管理器:

	//import org.crazycake.shiro.RedisCacheManager;包的RedisCacheManager的對象
    @Bean("redisCacheManager")
    public RedisCacheManager redisCacheManager(){
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

     ShiroConfig的Redis配置管理器:

    @Bean
    public RedisManager redisManager(){
        RedisManager redisManager = new RedisManager();
        redisManager.setHost("192.168.2.104");
        redisManager.setPort(6379);
        //redisManager.setPassword("123456");
        return redisManager;
    }

     ShiroConfig的Redis會話管理器:

@Bean("redisSessionManager")
    public SessionManager redisSessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        Collection<SessionListener> listeners = new ArrayList<SessionListener>();
        //配置監聽
        listeners.add(sessionListener());
        sessionManager.setSessionListeners(listeners);
        sessionManager.setSessionIdCookie(sessionIdCookie());
        sessionManager.setSessionDAO(redisSessionDAO());
        sessionManager.setCacheManager(redisCacheManager());
        //sessionManager.setGlobalSessionTimeout(60000);    //全局會話超時時間(單位毫秒),默認30分鐘  暫時設置爲10秒鐘 用來測試
        sessionManager.setDeleteInvalidSessions(true);
        //取消url 後面的 JSESSIONID
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;

    }

     ShiroConfig的redisSessionDao:

    @Bean("redisSessionDAO")
    public SessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        redisSessionDAO.setExpire(3000);//session在redis中的保存時間,最好大於session會話超時時間,單位s
        return redisSessionDAO;
    }

     自定義Realm類修改認證方法返回的info:

		SimpleAuthenticationInfo authenticationInfo = 
//				new SimpleAuthenticationInfo(user,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),"shiroRealm");
		new SimpleAuthenticationInfo(user,user.getPassword(),new MySimpleByteSource(user.getSalt()),"shiroRealm");

     新增的MySimpleByteSource類:

/**
 * 解決:
 *  shiro 使用緩存時出現:java.io.NotSerializableException: org.apache.shiro.util.SimpleByteSource
 *  序列化後,無法反序列化的問題
 */
public class MySimpleByteSource implements ByteSource, Serializable {
    private static final long serialVersionUID = 5175082362119580768L;

    private  byte[] bytes;
    private String cachedHex;
    private String cachedBase64;

    public MySimpleByteSource(){
    }

    public MySimpleByteSource(byte[] bytes) {
        this.bytes = bytes;
    }

    public MySimpleByteSource(char[] chars) {
        this.bytes = CodecSupport.toBytes(chars);
    }

    public MySimpleByteSource(String string) {
        this.bytes = CodecSupport.toBytes(string);
    }

    public MySimpleByteSource(ByteSource source) {
        this.bytes = source.getBytes();
    }

    public MySimpleByteSource(File file) {
        this.bytes = (new BytesHelper()).getBytes(file);
    }

    public MySimpleByteSource(InputStream stream) {
        this.bytes = (new BytesHelper()).getBytes(stream);
    }

    public static boolean isCompatible(Object o) {
        return o instanceof byte[] || o instanceof char[] || o instanceof String || o instanceof ByteSource || o instanceof File || o instanceof InputStream;
    }

    public void setBytes(byte[] bytes) {
        this.bytes = bytes;
    }

    @Override
    public byte[] getBytes() {
        return this.bytes;
    }


    @Override
    public String toHex() {
        if(this.cachedHex == null) {
            this.cachedHex = Hex.encodeToString(this.getBytes());
        }
        return this.cachedHex;
    }

    @Override
    public String toBase64() {
        if(this.cachedBase64 == null) {
            this.cachedBase64 = Base64.encodeToString(this.getBytes());
        }

        return this.cachedBase64;
    }

    @Override
    public boolean isEmpty() {
        return this.bytes == null || this.bytes.length == 0;
    }
    @Override
    public String toString() {
        return this.toBase64();
    }

    @Override
    public int hashCode() {
        return this.bytes != null && this.bytes.length != 0? Arrays.hashCode(this.bytes):0;
    }

    @Override
    public boolean equals(Object o) {
        if(o == this) {
            return true;
        } else if(o instanceof ByteSource) {
            ByteSource bs = (ByteSource)o;
            return Arrays.equals(this.getBytes(), bs.getBytes());
        } else {
            return false;
        }
    }

    private static final class BytesHelper extends CodecSupport {
        private BytesHelper() {
        }

        public byte[] getBytes(File file) {
            return this.toBytes(file);
        }

        public byte[] getBytes(InputStream stream) {
            return this.toBytes(stream);
        }
    }

主要問題:

     1).java.io.NotSerializableException: org.apache.shiro.util.SimpleByteSource異常:

          因爲認證方法返回info對象第三個參數ByteSource類型,並沒有實現序列化接口,所以序列化的時候出現異常,
          解決: 自定義一個類實現ByteSource和Serializable接口,見上文MySimpleByteSource

     2).com.demo.entity.User cannot be cast to com.demo.entity.User轉換異常:
          一看,同樣類型爲什麼轉換不成呢?因爲SpringBoot項目配了熱部署。
          解決: 註釋掉熱部署依賴。Clear一下就好了。

<dependency>
   	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
    <scope>true</scope>
</dependency>

     3). org.crazycake.shiro.exception.PrincipalInstanceException: class com.demo.entity.User must has getter for field: authCacheKey or id無法獲取緩存中權限redisKey。
          因爲shiro-redis3.1.0裏面,有個條件寫死的。本人的User類不滿足條件。
在這裏插入圖片描述
     可見,User類要有AuthCacheKey或者Id屬性,也就是要有getAuthCacheKey()或getId()方法,條件才能滿足,下面會通過映射獲取屬性值。不滿足就拋異常了。

          解決: 也行換個包可以解決,要麼就修改User類,但改動會有點大,要麼就換一種Redis的實現方式,也就是不用Shiro-Redis的包。可查看下一篇Shiro功能應用(八)–Shiro集成RedisTemplate(SDR)

     4).RedisCache纔是主要操作redis緩存的操作類, 代碼中任何出現操作緩存(get、put)之類的,都會到這個類操作

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章