Redis應用

1. 定義

  redis是一個key-value存儲系統。和Memcached相似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操做,並且這些操做都是原子性的。在此基礎上,redis支持各類不一樣方式的排序。與memcached同樣,爲了保證效率,數據都是緩存在內存中。區別的是redis會週期性的把更新的數據寫入磁盤或者把修改操做寫入追加的記錄文件,而且在此基礎上實現了master-slave(主從)同步。java

2. Redis在用戶登陸時的應用

  正式接觸Redis是作用戶登陸控制。有個場景,每一個用戶登陸時後臺生成惟一Token,保存至Redis中,並將此Token返回給前臺。此後,前臺每次發送請求時在URL的Header中攜帶此Token,後臺驗證此Token是否在Redis中存在,存在即驗證經過。大體需求完成後,有一些小細節須要處理。例如,要求用戶2小時不操做頁面便自動退出;後臺日誌要記錄每位用戶登陸登出時間;業務方面要求標籤信息存儲在緩存中,便於標籤的交集,並集查詢;此時,Redis就派上用場了。web

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工具類。redis

  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 }
Jwt生成Token工具類
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);
    }

}
RedisUtil工具類

2.1.2 SpringBoot請求攔截控制 

  用戶登陸時,驗證用戶名密碼經過後。使用JWT生成惟一Token。保存至Redis中(key->生成的sessionID,value->生成的Token),設置Redis過時時間爲2小時。之後每次用戶發送請求時,都會攔截此Token。若Redis中不存在此Token,返回用戶登陸過時錯誤。若存在Token,會更新此Token過時時間爲2小時。這樣,解決了第一個問題,要求用戶2小時不操做頁面便自動退出。下面貼一個用戶登陸及請求攔截代碼。spring

 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     }
登陸代碼
 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 }
SpringBoot請求攔截

2.2 Redis中key過時監聽

  後臺日誌系統要記錄用戶登陸退出操做。對於正常退出用戶能夠在退出時寫日誌到數據庫,非正常退出用戶就沒法記錄日誌。使用Redis的key過時監聽功能,後臺訂閱Redis的過時消息通知,Token過時時,後臺就會監聽此token,經過JWT解析出用戶ID和sessionID,寫入日誌。這樣就解決了第二個問題,後臺日誌要記錄每位用戶登陸登出時間。下面貼一個Redis的Key過時監聽配置。數據庫

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);
        }
    }
}
Redis過時監聽
 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 }
Redis過時監聽

2.3  項目初始化時將數據庫指定數據返回Redis中

  這次操做基本設計業務操做,此處便不作詳細介紹。下面貼一個SpringBoot初始化執行業務的接口。apache

 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 }
SpringBoot初始化

3. 總結

  關於Redis集羣在liunx環境中配置及Java配置Redis,你們請自行百度--- 因爲這次是本身第一次寫Blog,有不少寫的很差的地方,願見諒。本文章全是手打,項目來自本身的小Demo。期待本身下次寫會有進步-----跨域

相關文章
相關標籤/搜索