Redis应用
1. 定义
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
2. Redis在用户登录时的应用
正式接触Redis是做用户登录控制。有个场景,每个用户登录时后台生成唯一Token,保存至Redis中,并将此Token返回给前台。此后,前台每次发送请求时在URL的Header中携带此Token,后台验证此Token是否在Redis中存在,存在即验证通过。大致需求完成后,有一些小细节需要处理。例如,要求用户2小时不操作页面便自动退出;后台日志要记录每位用户登录登出时间;业务方面要求标签信息存储在缓存中,便于标签的交集,并集查询;此时,Redis就派上用场了。
2.1 Redis保存用户登录Token
2.1.1 JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案
使用JSON Web Token(JWT)根据随机sessionID,用户名ID,用户名Name生成唯一Token。关于JWT详细介绍,请自行百度---;此次大致理解为用一串随机数,ID,Name生成一个密码,此密码就是token,密码可以解码获取原先的sessionID,ID,Name;下面贴一个我自己常用的JWT工具类和简单Redis工具类。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.boot.util; 2 3 import com.auth0.jwt.JWT; 4 import com.auth0.jwt.algorithms.Algorithm; 5 import com.auth0.jwt.exceptions.JWTCreationException; 6 import com.auth0.jwt.exceptions.JWTDecodeException; 7 import com.auth0.jwt.interfaces.DecodedJWT; 8 import org.apache.log4j.Logger; 9 10 import java.io.UnsupportedEncodingException; 11 import java.util.Date; 12 13 public class JwtParseUtil { 14 private static Logger LOGGER = Logger.getLogger(JwtParseUtil.class); 15 16 public static final String createJWT(String userName,long sessionID,int userId){ 17 String token = null; 18 try{ 19 //参数非空判断 20 Algorithm alg = Algorithm.HMAC256(userName); 21 token = JWT.create() 22 .withJWTId(String.valueOf(sessionID)) 23 .withSubject(userName) 24 .withClaim("userId", userId) 25 .withIssuedAt(new Date()) 26 .sign(alg); 27 }catch(UnsupportedEncodingException unsuptEncode){ 28 LOGGER.info("不支持UTF-8编码"); 29 //UTF-8 encoding not supported 30 }catch(JWTCreationException jwtCreateExp){ 31 LOGGER.info("签名配置无效,或者是无法转换请求"); 32 //Invalid Signing configuration / Couldn't convert Claims. 33 } 34 return token; 35 36 } 37 /** 38 * token解析 39 * */ 40 private static DecodedJWT parseJWT(String token){ 41 try{ 42 DecodedJWT jwt= JWT.decode(token); 43 return jwt; 44 }catch(JWTDecodeException exp){ 45 //Invalid token 46 return null; 47 } 48 } 49 50 /** 51 * 根据token获取用户名 52 * */ 53 public static String getUserNameByJWT(String token){ 54 if (token==null) { 55 throw new MessageException(MessageException.USER_INFO_OUTTIME,null); 56 } 57 DecodedJWT jwt = parseJWT(token); 58 if( jwt != null ){ 59 return jwt.getSubject(); 60 }else{ 61 return null; 62 } 63 } 64 65 /** 66 * 根据token获取sessionId 67 */ 68 public static long getSessionIdByJWT(String token){ 69 if(token == null){ 70 return 0; 71 } 72 DecodedJWT jwt = parseJWT(token); 73 if( jwt != null ){ 74 return Long.valueOf(jwt.getId()); 75 }else{ 76 return 0; 77 } 78 79 } 80 81 public static int getUserIdByJWT(String token){ 82 if (token==null) { 83 throw new MessageException(MessageException.USER_INFO_OUTTIME,null); 84 } 85 DecodedJWT jwt = parseJWT(token); 86 if( jwt != null ){ 87 return jwt.getClaim("userId").asInt(); 88 }else{ 89 return 0; 90 } 91 } 92 93 /** 94 * 生成唯一的sessionID 95 * @return 96 */ 97 public static long createSessionId(){ 98 99 long now = System.currentTimeMillis();//一个13位的时间戳 100 101 int r=(int)((Math.random()*9+1)*10000); 102 103 String paymentID =String.valueOf(now)+String.valueOf(r);// sessionID 104 return Long.valueOf(paymentID); 105 106 } 107 108 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.DataType; import org.springframework.data.redis.core.*; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.stereotype.Component; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @Component public class RedisUtil { @Autowired private RedisTemplate redisTemplate; /** * 批量删除对应的value * * @param keys */ public void remove(final String... keys) { for (String key : keys) { remove(key); } } /** * 批量删除key * * @param pattern */ public void removePattern(final String pattern) { Set<Serializable> keys = redisTemplate.keys(pattern); if (keys.size() > 0) redisTemplate.delete(keys); } /** * 删除对应的value * * @param key */ public void remove(final String key) { if (exists(key)) { redisTemplate.delete(key); } } /** * 判断缓存中是否有对应的value * * @param key * @return */ public boolean exists(final String key) { return redisTemplate.hasKey(key); } /** * 读取缓存 * * @param key * @return */ public String get(final String key) { Object result = null; redisTemplate.setValueSerializer(new StringRedisSerializer()); ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); result = operations.get(key); if (result == null) { return null; } return result.toString(); } /** * 写入缓存 * * @param key * @param value * @return */ public boolean set(final String key, Object value) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } /** * 写入缓存 * * @param key * @param value * @return */ public boolean set(final String key, Object value, Long expireTime) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } public boolean hmset(String key, Map<String, String> value) { boolean result = false; try { redisTemplate.opsForHash().putAll(key, value); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } public Map<String, String> hmget(String key) { Map<String, String> result = null; try { result = redisTemplate.opsForHash().entries(key); } catch (Exception e) { e.printStackTrace(); } return result; } /** * @Author xiabing5 * @Create 2019/8/26 19:33 * @Desc 查找匹配的key **/ public Set<String> keys(String pattern) { return redisTemplate.keys(pattern); } /** * @Author xiabing5 * @Create 2019/8/26 19:33 * @Desc 移除key过期时间,key将持久保持 **/ public Boolean persist(String key) { return redisTemplate.persist(key); } /** * @Author xiabing5 * @Create 2019/8/26 19:33 * @Desc 返回key的剩余过期时间 **/ public Long getExpire(String key, TimeUnit unit) { return redisTemplate.getExpire(key, unit); } /** * @Author xiabing5 * @Create 2019/8/26 19:33 * @Desc 返回key的剩余过期时间 **/ public Long getExpire(String key) { return redisTemplate.getExpire(key); } /** * @Author xiabing5 * @Create 2019/8/26 19:33 * @Desc 从当前数据库随机返回一个key **/ public String randomKey() { return (String) redisTemplate.randomKey(); } /** * 修改 key 的名称 * * @param oldKey * @param newKey */ public void rename(String oldKey, String newKey) { redisTemplate.rename(oldKey, newKey); } /** * 仅当 newkey 不存在时,将 oldKey 改名为 newkey * * @param oldKey * @param newKey * @return */ public Boolean renameIfAbsent(String oldKey, String newKey) { return redisTemplate.renameIfAbsent(oldKey, newKey); } /** * 返回 key 所储存的值的类型 * * @param key * @return */ public DataType type(String key) { return redisTemplate.type(key); } /** * 批量获取 * * @param keys * @return */ public List<String> multiGet(Collection<String> keys) { return redisTemplate.opsForValue().multiGet(keys); } /** --------------------set相关操作-------------------------- */ /** * set添加元素 * * @param key * @param values * @return */ public Long sAdd(String key, String... values) { return redisTemplate.opsForSet().add(key, values); } /** * set移除元素 * * @param key * @param values * @return */ public Long sRemove(String key, Object... values) { return redisTemplate.opsForSet().remove(key, values); } /** * 移除并返回集合的一个随机元素 * * @param key * @return */ public String sPop(String key) { return (String)redisTemplate.opsForSet().pop(key); } /** * 将元素value从一个集合移到另一个集合 * * @param key * @param value * @param destKey * @return */ public Boolean sMove(String key, String value, String destKey) { return redisTemplate.opsForSet().move(key, value, destKey); } /** * 获取集合的大小 * * @param key * @return */ public Long sSize(String key) { return redisTemplate.opsForSet().size(key); } /** * 判断集合是否包含value * * @param key * @param value * @return */ public Boolean sIsMember(String key, Object value) { return redisTemplate.opsForSet().isMember(key, value); } /** * 获取两个集合的交集 * * @param key * @param otherKey * @return */ public Set<String> sIntersect(String key, String otherKey) { return redisTemplate.opsForSet().intersect(key, otherKey); } /** * 获取key集合与多个集合的交集 * * @param key * @param otherKeys * @return */ public Set<String> sIntersect(String key, Collection<String> otherKeys) { return redisTemplate.opsForSet().intersect(key, otherKeys); } /** * key集合与otherKey集合的交集存储到destKey集合中 * * @param key * @param otherKey * @param destKey * @return */ public Long sIntersectAndStore(String key, String otherKey, String destKey) { return redisTemplate.opsForSet().intersectAndStore(key, otherKey, destKey); } /** * key集合与多个集合的交集存储到destKey集合中 * * @param key * @param otherKeys * @param destKey * @return */ public Long sIntersectAndStore(String key, Collection<String> otherKeys, String destKey) { return redisTemplate.opsForSet().intersectAndStore(key, otherKeys, destKey); } /** * 获取两个集合的并集 * * @param key * @param otherKeys * @return */ public Set<String> sUnion(String key, String otherKeys) { return redisTemplate.opsForSet().union(key, otherKeys); } /** * 获取key集合与多个集合的并集 * * @param key * @param otherKeys * @return */ public Set<String> sUnion(String key, Collection<String> otherKeys) { return redisTemplate.opsForSet().union(key, otherKeys); } /** * key集合与otherKey集合的并集存储到destKey中 * * @param key * @param otherKey * @param destKey * @return */ public Long sUnionAndStore(String key, String otherKey, String destKey) { return redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey); } /** * key集合与多个集合的并集存储到destKey中 * * @param key * @param otherKeys * @param destKey * @return */ public Long sUnionAndStore(String key, Collection<String> otherKeys, String destKey) { return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey); } /** * 获取两个集合的差集 * * @param key * @param otherKey * @return */ public Set<String> sDifference(String key, String otherKey) { return redisTemplate.opsForSet().difference(key, otherKey); } /** * 获取key集合与多个集合的差集 * * @param key * @param otherKeys * @return */ public Set<String> sDifference(String key, Collection<String> otherKeys) { return redisTemplate.opsForSet().difference(key, otherKeys); } /** * key集合与otherKey集合的差集存储到destKey中 * * @param key * @param otherKey * @param destKey * @return */ public Long sDifference(String key, String otherKey, String destKey) { return redisTemplate.opsForSet().differenceAndStore(key, otherKey, destKey); } /** * key集合与多个集合的差集存储到destKey中 * * @param key * @param otherKeys * @param destKey * @return */ public Long sDifference(String key, Collection<String> otherKeys, String destKey) { return redisTemplate.opsForSet().differenceAndStore(key, otherKeys, destKey); } /** * 获取集合所有元素 * * @param key * @param * @param * @return */ public Set<String> setMembers(String key) { return redisTemplate.opsForSet().members(key); } /** * 随机获取集合中的一个元素 * * @param key * @return */ public String sRandomMember(String key) { return (String) redisTemplate.opsForSet().randomMember(key); } /** * 随机获取集合中count个元素 * * @param key * @param count * @return */ public List<String> sRandomMembers(String key, long count) { return redisTemplate.opsForSet().randomMembers(key, count); } /** * 随机获取集合中count个元素并且去除重复的 * * @param key * @param count * @return */ public Set<String> sDistinctRandomMembers(String key, long count) { return redisTemplate.opsForSet().distinctRandomMembers(key, count); } /** * * @param key * @param options * @return */ public Cursor<String> sScan(String key, ScanOptions options) { return redisTemplate.opsForSet().scan(key, options); } /**------------------zSet相关操作--------------------------------*/ /** * 添加元素,有序集合是按照元素的score值由小到大排列 * * @param key * @param value * @param score * @return */ public Boolean zAdd(String key, String value, double score) { return redisTemplate.opsForZSet().add(key, value, score); } /** * * @param key * @param values * @return */ public Long zAdd(String key, Set<ZSetOperations.TypedTuple<String>> values) { return redisTemplate.opsForZSet().add(key, values); } /** * * @param key * @param values * @return */ public Long zRemove(String key, Object... values) { return redisTemplate.opsForZSet().remove(key, values); } /** * 增加元素的score值,并返回增加后的值 * * @param key * @param value * @param delta * @return */ public Double zIncrementScore(String key, String value, double delta) { return redisTemplate.opsForZSet().incrementScore(key, value, delta); } /** * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列 * * @param key * @param value * @return 0表示第一位 */ public Long zRank(String key, Object value) { return redisTemplate.opsForZSet().rank(key, value); } /** * 返回元素在集合的排名,按元素的score值由大到小排列 * * @param key * @param value * @return */ public Long zReverseRank(String key, Object value) { return redisTemplate.opsForZSet().reverseRank(key, value); } /** * 获取集合的元素, 从小到大排序 * * @param key * @param start * 开始位置 * @param end * 结束位置, -1查询所有 * @return */ public Set<String> zRange(String key, long start, long end) { return redisTemplate.opsForZSet().range(key, start, end); } /** * 获取集合元素, 并且把score值也获取 * * @param key * @param start * @param end * @return */ public Set<ZSetOperations.TypedTuple<String>> zRangeWithScores(String key, long start, long end) { return redisTemplate.opsForZSet().rangeWithScores(key, start, end); } /** * 根据Score值查询集合元素 * * @param key * @param min * 最小值 * @param max * 最大值 * @return */ public Set<String> zRangeByScore(String key, double min, double max) { return redisTemplate.opsForZSet().rangeByScore(key, min, max); } /** * 根据Score值查询集合元素, 从小到大排序 * * @param key * @param min * 最小值 * @param max * 最大值 * @return */ public Set<ZSetOperations.TypedTuple<String>> zRangeByScoreWithScores(String key, double min, double max) { return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max); } /** * * @param key * @param min * @param max * @param start * @param end * @return */ public Set<ZSetOperations.TypedTuple<String>> zRangeByScoreWithScores(String key, double min, double max, long start, long end) { return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max, start, end); } /** * 获取集合的元素, 从大到小排序 * * @param key * @param start * @param end * @return */ public Set<String> zReverseRange(String key, long start, long end) { return redisTemplate.opsForZSet().reverseRange(key, start, end); } /** * 获取集合的元素, 从大到小排序, 并返回score值 * * @param key * @param start * @param end * @return */ public Set<ZSetOperations.TypedTuple<String>> zReverseRangeWithScores(String key, long start, long end) { return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end); } /** * 根据Score值查询集合元素, 从大到小排序 * * @param key * @param min * @param max * @return */ public Set<String> zReverseRangeByScore(String key, double min, double max) { return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max); } /** * 根据Score值查询集合元素, 从大到小排序 * * @param key * @param min * @param max * @return */ public Set<ZSetOperations.TypedTuple<String>> zReverseRangeByScoreWithScores( String key, double min, double max) { return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, min, max); } /** * * @param key * @param min * @param max * @param start * @param end * @return */ public Set<String> zReverseRangeByScore(String key, double min, double max, long start, long end) { return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, start, end); } /** * 根据score值获取集合元素数量 * * @param key * @param min * @param max * @return */ public Long zCount(String key, double min, double max) { return redisTemplate.opsForZSet().count(key, min, max); } /** * 获取集合大小 * * @param key * @return */ public Long zSize(String key) { return redisTemplate.opsForZSet().size(key); } /** * 获取集合大小 * * @param key * @return */ public Long zZCard(String key) { return redisTemplate.opsForZSet().zCard(key); } /** * 获取集合中value元素的score值 * * @param key * @param value * @return */ public Double zScore(String key, Object value) { return redisTemplate.opsForZSet().score(key, value); } /** * 移除指定索引位置的成员 * * @param key * @param start * @param end * @return */ public Long zRemoveRange(String key, long start, long end) { return redisTemplate.opsForZSet().removeRange(key, start, end); } /** * 根据指定的score值的范围来移除成员 * * @param key * @param min * @param max * @return */ public Long zRemoveRangeByScore(String key, double min, double max) { return redisTemplate.opsForZSet().removeRangeByScore(key, min, max); } /** * 获取key和otherKey的并集并存储在destKey中 * * @param key * @param otherKey * @param destKey * @return */ public Long zUnionAndStore(String key, String otherKey, String destKey) { return redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey); } /** * * @param key * @param otherKeys * @param destKey * @return */ public Long zUnionAndStore(String key, Collection<String> otherKeys, String destKey) { return redisTemplate.opsForZSet() .unionAndStore(key, otherKeys, destKey); } /** * 交集 * * @param key * @param otherKey * @param destKey * @return */ public Long zIntersectAndStore(String key, String otherKey, String destKey) { return redisTemplate.opsForZSet().intersectAndStore(key, otherKey, destKey); } /** * 交集 * * @param key * @param otherKeys * @param destKey * @return */ public Long zIntersectAndStore(String key, Collection<String> otherKeys, String destKey) { return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys, destKey); } /** * * @param key * @param options * @return */ public Cursor<ZSetOperations.TypedTuple<String>> zScan(String key, ScanOptions options) { return redisTemplate.opsForZSet().scan(key, options); } }
2.1.2 SpringBoot请求拦截控制
用户登录时,验证用户名密码通过后。使用JWT生成唯一Token。保存至Redis中(key->生成的sessionID,value->生成的Token),设置Redis过期时间为2小时。以后每次用户发送请求时,都会拦截此Token。若Redis中不存在此Token,返回用户登录过期错误。若存在Token,会更新此Token过期时间为2小时。这样,解决了第一个问题,要求用户2小时不操作页面便自动退出。下面贴一个用户登录及请求拦截代码。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public String userLogin(UserInfo userInfo) { 2 3 String oaName = userInfo.getOaName(); 4 String pswd = userInfo.getPswd(); 5 //判断用户名密码不为空 6 if(oaName == null || "".equals(oaName) || pswd == null || "".equals(pswd)){ 7 throw new MessageException(MessageException.NO_EMPTY,MessageException.NO_EMPTY_MESSAGE); 8 } 9 //数据库验证 10 List<UserInfo> userInfos = userInfoMapper.getUserByLogin(oaName,pswd); 11 if(userInfos.size() == 0) { 12 // 抛出自定义异常 13 throw new MessageException(MessageException.LOGGING_ERROR,MessageException.LOGGING_ERROR_MESSAGE); 14 } 15 //生成session,token 16 Long sessionID = JwtParseUtil.createSessionId(); 17 System.out.println("####登录用户session:"+sessionID+"####"); 18 String token = JwtParseUtil.createJWT(userInfos.get(0).getOaName(),sessionID,userInfos.get(0).getUserId()); 19 20 // 获取登录IP(自己写的登录日志记录功能,可忽略) 21 String ipAddress = IpUtil.getIpAddress(getHttpServletRequest()); 22 23 // 插入登录日志中 24 UserLoginStatistics userLoginStatistics = new UserLoginStatistics(sessionID,userInfos.get(0).getUserId(),new Date().getTime(),ipAddress,null); 25 userLoginStatisticsMapper.insert(userLoginStatistics); 26 27 // 用户Token存放在Redis中,Constant.TOKEN_EXPIRE_TIME为7200L,即2小时 28 29 redisUtil.set(sessionID+"_UTOKEN",token, Constant.TOKEN_EXPIRE_TIME); 30 31 32 return token; 33 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.boot.beanConfig; 2 3 import com.boot.util.IpUtil; 4 import com.boot.util.JwtParseUtil; 5 import com.boot.util.MessageException; 6 import com.boot.util.RedisUtil; 7 import org.springframework.beans.factory.BeanFactory; 8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.context.annotation.Configuration; 10 import org.springframework.web.context.support.WebApplicationContextUtils; 11 import org.springframework.web.servlet.HandlerInterceptor; 12 import org.springframework.web.servlet.ModelAndView; 13 import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 14 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 15 16 import javax.annotation.Resource; 17 import javax.servlet.http.HttpServletRequest; 18 import javax.servlet.http.HttpServletResponse; 19 20 import static com.boot.util.MessageException.SYSTEM_UPDATE; 21 import static com.boot.util.MessageException.SYSTEM_UPDATE_MESSAGE; 22 23 /** 24 * @Author xiabing5 25 * @Create 2019/7/11 19:04 26 * @Desc 访问拦截器配置 27 **/ 28 @Configuration 29 public class WebInterceptorConfig extends WebMvcConfigurerAdapter { 30 @Resource 31 private RedisUtil redisUtil; 32 33 @Override 34 public void addInterceptors(InterceptorRegistry registry) { 35 36 //注册自定义拦截器,添加拦截路径和排除拦截路径 37 registry.addInterceptor(new RequestInterceptorConfig()).addPathPatterns("/RestService/**").excludePathPatterns("/RestService/userRestService/userLogin","/RestService/userRestService/forgetPassword","/RestService/userRestService/updateOwnPswd"); 38 } 39 40 /** 41 * @Author xiabing5 42 * @Create 2019/7/11 19:33 43 * @Desc token验证 44 **/ 45 public class RequestInterceptorConfig implements HandlerInterceptor { 46 47 @Override 48 public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { 49 // 防止复杂请求先发的options请求被拦截 50 if("OPTIONS".equals(httpServletRequest.getMethod())) { 51 return true; 52 } 53 54 System.out.println("#######进入权限验证#######"); 55 String token = httpServletRequest.getHeader("token"); 56 57 Long sessionID = JwtParseUtil.getSessionIdByJWT(token); 58 if( !redisUtil.exists(sessionID+"_UTOKEN")){ 59 throw new MessageException(MessageException.USER_INFO_OUTTIME,MessageException.USER_INFO_OUTTIME_MESSAGE); 60 } 61 62 if(token == null || "".equals(token)) { 63 System.out.println("#######被拦截#######"); 64 throw new MessageException(MessageException.NO_LOGIN,MessageException.NO_LOGIN_MESSAGE); 65 } 66 return true; 67 } 68 69 @Override 70 public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { 71 72 } 73 74 @Override 75 public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { 76 77 } 78 } 79 80 }
2.2 Redis中key过期监听
后台日志系统要记录用户登录退出操作。对于正常退出用户可以在退出时写日志到数据库,非正常退出用户就无法记录日志。使用Redis的key过期监听功能,后台订阅Redis的过期消息通知,Token过期时,后台就会监听此token,通过JWT解析出用户ID和sessionID,写入日志。这样就解决了第二个问题,后台日志要记录每位用户登录登出时间。下面贴一个Redis的Key过期监听配置。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.boot.listen; import com.boot.mapper.UserLoginStatisticsMapper; import com.boot.pojo.UserLoginStatistics; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.listener.KeyExpirationEventMessageListener; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.stereotype.Component; import java.util.Date; /** * @Author xiabing5 * @Create 2019/8/6 14:53 * @Desc 过期监听操作 **/ @Component public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener { @Autowired private UserLoginStatisticsMapper userLoginStatisticsMapper; public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); } @Override public void onMessage(Message message, byte[] pattern) { // 用户做自己的业务处理即可,message.toString()可以获取失效的key String expiredKey = message.toString(); // 捕获超时退出的token if(expiredKey.contains("_UTOKEN")) { String session = expiredKey.substring(expiredKey.indexOf("1"),expiredKey.indexOf("_")); if(session != null) { try{ Long sessionId = Long.valueOf(session); UserLoginStatistics userLoginStatistics = new UserLoginStatistics(sessionId,null,null,null,new Date().getTime()); userLoginStatisticsMapper.update(userLoginStatistics); }catch (Exception e) { e.printStackTrace(); } } System.out.println("#########清除你#########"+session); } } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.boot.beanConfig; 2 3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.data.redis.connection.RedisConnectionFactory; 6 import org.springframework.data.redis.listener.PatternTopic; 7 import org.springframework.data.redis.listener.RedisMessageListenerContainer; 8 9 /** 10 * @Author xiabing5 11 * @Create 2019/8/6 14:46 12 * @Desc 监听redis中Key过期事件 13 **/ 14 @Configuration 15 public class RedisListenerConfig { 16 @Bean 17 RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { 18 19 RedisMessageListenerContainer container = new RedisMessageListenerContainer(); 20 container.setConnectionFactory(connectionFactory); 21 //container.addMessageListener(new RedisKeyExpirationListener(container), new PatternTopic("__keyevent@0__:expired")); 22 return container; 23 } 24 }
2.3 项目初始化时将数据库指定数据返回Redis中
此次操作基本设计业务操作,此处便不做详细介绍。下面贴一个SpringBoot初始化执行业务的接口。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.boot.beanConfig; 2 3 import org.springframework.boot.CommandLineRunner; 4 import org.springframework.stereotype.Component; 5 6 /** 7 * @Author xiabing5 8 * @Create 2019/8/27 10:04 9 * @Desc 容器启动时执行方法 10 **/ 11 @Component 12 public class InitRunner implements CommandLineRunner{ 13 14 @Override 15 public void run(String... strings) throws Exception { 16 System.out.println("###执行方法###"); 17 } 18 19 // 存放lable的key-value键值对 20 private static void putInfoToRedis() { 21 22 } 23 }
3. 总结
关于Redis集群在liunx环境中配置及Java配置Redis,大家请自行百度--- 由于此次是自己第一次写Blog,有很多写的不好的地方,愿见谅。本文章全是手打,项目来自自己的小Demo。期待自己下次写会有进步-----