7.Shiro中使用reids作为缓存实现
1.引入redis依赖
<!--redis整合springboot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置redis连接
spring.redis.port=6379
spring.redis.host=localhost
spring.redis.database=0
3.开发RedisCacheManager
自定义shiro缓存管理器
package com.chif.shiro.config.cache;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
//自定义shiro缓存管理器
public class RedisCacheManager implements CacheManager {
//参数1:认证或者是授权缓存的统一名称 s
@Override
public <K, V> Cache<K, V> getCache(String s) throws CacheException {
return new RedisCache<K,V>(s);
}
}
4.RedisCache实现
自定义redis缓存的实现
package com.chif.shiro.config.cache;
import com.chif.shiro.utils.ApplicationContextUtils;
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;
//自定义redis缓存的实现
public class RedisCache<k,v> implements Cache<k,v>{
private String cacheName;
public RedisCache(){
}
public RedisCache(String cacheName){
this.cacheName=cacheName;
}
//获取redis操作对象
public RedisTemplate getRedisTemplate(){
RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
@Override
public v get(k k) throws CacheException {
System.out.println("get key"+k);
return (v) getRedisTemplate().opsForHash().get(this.cacheName,k.toString());
}
@Override
public v put(k k, v v) throws CacheException {
System.out.println("put key: "+k);
System.out.println("put value: "+v);
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().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);
}
}
序列化问题
-
caused by: java.io.NotSerializableException: org.apache.shiro.util.SimpleByteSource
-
错误解释: 由于shiro中提供的simpleByteSource实现没有实现序列化,所有在认证时出现错误信息
-
解决方案: 需要自动salt实现序列化
package com.chif.shiro.config.salt; import org.apache.shiro.codec.Base64; import org.apache.shiro.codec.CodecSupport; import org.apache.shiro.codec.Hex; import org.apache.shiro.util.ByteSource; import java.io.File; import java.io.InputStream; import java.io.Serializable; import java.util.Arrays; public class MyByteSource implements ByteSource, Serializable { private byte[] bytes; private String cachedHex; private String cachedBase64; public MyByteSource(){ } public MyByteSource(byte[] bytes) { this.bytes = bytes; } public MyByteSource(char[] chars) { this.bytes = CodecSupport.toBytes(chars); } public MyByteSource(String string) { this.bytes = CodecSupport.toBytes(string); } public MyByteSource(ByteSource source) { this.bytes = source.getBytes(); } public MyByteSource(File file) { this.bytes = (new com.chif.shiro.config.salt.MyByteSource.BytesHelper()).getBytes(file); } public MyByteSource(InputStream stream) { this.bytes = (new com.chif.shiro.config.salt.MyByteSource.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 byte[] getBytes() { return this.bytes; } public boolean isEmpty() { return this.bytes == null || this.bytes.length == 0; } public String toHex() { if (this.cachedHex == null) { this.cachedHex = Hex.encodeToString(this.getBytes()); } return this.cachedHex; } public String toBase64() { if (this.cachedBase64 == null) { this.cachedBase64 = Base64.encodeToString(this.getBytes()); } return this.cachedBase64; } public String toString() { return this.toBase64(); } public int hashCode() { return this.bytes != null && this.bytes.length != 0 ? Arrays.hashCode(this.bytes) : 0; } 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); } } }
-
在realm中使用自定义salt
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("=========================="); //根据身份信息 String principal = (String) token.getPrincipal(); //在工厂中获取service对象 UserService userService = (UserService) ApplicationContextUtils.getBean("userService"); User user = userService.findByUserName(principal); if(!ObjectUtils.isEmpty(user)){ return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), new MyByteSource(user.getSalt()),this.getName()); } return null; }