1.為什麼要使用cache
用來減輕資料庫的訪問壓力,從而提升查詢效率。
2.Shiro使用Redis做快取
redis環境準備
參照程式碼工程li麵redis模組裡面docker,按照要求啟動即可
引入pom,xml
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
redis配置
spring: redis: database: 0 port: 6379 host: 127.0.0.1 password: 123456 timeout=3000: jedis: pool: max-active: 8 max-idle: 8 max-wait: -1 min-idle: 0
cache介面實現
建立RedisCacheManager實現CacheManager介面
package com.et.shiro.cache; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheException; import org.apache.shiro.cache.CacheManager; public class RedisCacheManager implements CacheManager { @Override public <K, V> Cache<K, V> getCache(String cacheKey) throws CacheException { return new RedisCache<>(cacheKey); } }
建立RedisCache實現Cache介面
package com.et.shiro.cache; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheException; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.util.Collection; import java.util.Set; public class RedisCache<K, V> implements Cache<K, V> { private String cacheName; public RedisCache() { } public RedisCache(String cacheName) { this.cacheName = cacheName; } private RedisTemplate getRedisTemplate() { RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate"); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); return redisTemplate; } @Override public V get(K k) throws CacheException { return (V) getRedisTemplate().opsForHash().get(this.cacheName,k.toString()); } @Override public V put(K k, V v) throws CacheException { getRedisTemplate().opsForHash().put(this.cacheName,k.toString(), v); return null; } @Override public V remove(K k) throws CacheException { return (V) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString()); } @Override public void clear() throws CacheException { getRedisTemplate().opsForHash().delete(this.cacheName); } @Override public int size() { return getRedisTemplate().opsForHash().size(this.cacheName).intValue(); } @Override public Set<K> keys() { return getRedisTemplate().opsForHash().keys(this.cacheName); } @Override public Collection<V> values() { return getRedisTemplate().opsForHash().values(this.cacheName); } }
獲取bean工具類
package com.et.shiro.cache; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class ApplicationContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public static <T> T getBean(String beanName) { return (T)applicationContext.getBean(beanName); } public static <T> T getBean(Class<T> className) { return applicationContext.getBean(className); } }
由於自定義realm中認證所需要的鹽值內部並沒有實現序列化介面,所以我們需要自己定一個MyByteSource繼承SimpleByteSource並實現Serializable介面
package com.et.shiro.config; import org.apache.shiro.util.SimpleByteSource; import java.io.Serializable; public class MyByteSource extends SimpleByteSource implements Serializable { public MyByteSource(String string) { super(string); } }
在自定義的Realm中需要在認證的方法中,改寫salt的處理。
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("MyShiroRealm.doGetAuthenticationInfo()"); String username = (String)token.getPrincipal(); System.out.println(token.getCredentials()); //query user by username //in here ,you can cache some data for efficient UserInfo userInfo = userInfoService.findByUsername(username); System.out.println("----->>userInfo="+userInfo); if(userInfo == null){ return null; } SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( userInfo, //username userInfo.getPassword(), //password new MyByteSource(userInfo.getCredentialsSalt()), //ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+salt getName() //realm name ); return authenticationInfo; }
注意實體類(角色類,使用者類,許可權類)要記得實現Serializable介面 最後在Shiro配置類中開啟快取,使用我們自己定義的RedisManager
@Bean public MyShiroRealm myShiroRealm(){ MyShiroRealm myShiroRealm = new MyShiroRealm(); myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());// 設定解密規則 // 開啟全域性快取 myShiroRealm.setCachingEnabled(true); // 開啟認證快取 myShiroRealm.setAuthenticationCachingEnabled(true); // 設定認證快取管理的名字 myShiroRealm.setAuthenticationCacheName("authenticationCache"); // 開啟授權快取管理 myShiroRealm.setAuthorizationCachingEnabled(true); // 設定授權快取管理的名字 myShiroRealm.setAuthorizationCacheName("authorizationCache"); // 開啟Redis快取 myShiroRealm.setCacheManager(new RedisCacheManager()); return myShiroRealm; }
以上只是一些關鍵程式碼,所有程式碼請參見下面程式碼倉庫
程式碼倉庫
https://github.com/Harries/springboot-demo
3.測試
啟動專案,訪問http://127.0.0.1:8088/userInfo/userAdd,會跳轉到登入頁,輸入admin 123456,檢視redis是否快取了
登入使用者,第一次會從資料庫中查詢,並透過RedisTemplate的put方法將使用者資訊裝入快取,下次再重新整理首頁就會從redis中查詢許可權,授權等資訊。退出時會呼叫RedisTemplate中的remove方法清除向對應的使用者快取。
4.引用
https://www.cnblogs.com/dataoblogs/p/14121884.html