<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    Spring Boot + Redis 搞定搜索欄熱搜、不雅文字過濾功能

    VSole2022-05-20 16:45:38

    使用java和redis實現一個簡單的熱搜功能,具備以下功能:

    1. 搜索欄展示當前登陸的個人用戶的搜索歷史記錄,刪除個人歷史記錄
    2. 用戶在搜索欄輸入某字符,則將該字符記錄下來 以zset格式存儲的redis中,記錄該字符被搜索的個數以及當前的時間戳 (用了DFA算法,感興趣的自己百度學習吧)
    3. 每當用戶查詢了已在redis存在了的字符時,則直接累加個數, 用來獲取平臺上最熱查詢的十條數據。(可以自己寫接口或者直接在redis中添加一些預備好的關鍵詞)
    4. 最后還要做不雅文字過濾功能。這個很重要不說了你懂的。

    代碼實現熱搜與個人搜索記錄功能,主要controller層下幾個方法就行了 :

    1. 向redis 添加熱搜詞匯(添加的時候使用下面不雅文字過濾的方法來過濾下這個詞匯,合法再去存儲
    2. 每次點擊給相關詞熱度 +1
    3. 根據key搜索相關最熱的前十名
    4. 插入個人搜索記錄
    5. 查詢個人搜索記錄

    首先配置好redis數據源等等基礎

    最后貼上核心的 服務層的代碼 :

    package com.****.****.****.user;
     
    import com.jianlet.service.user.RedisService;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.data.redis.core.*;
    import org.springframework.stereotype.Service;
    import javax.annotation.Resource;
    import java.util.*;
    import java.util.concurrent.TimeUnit;
     
    /**
     * @author: mrwanghc
     * @date: 2020/5/13
     * @description:
     */
    @Transactional
    @Service("redisService")
    public class RedisServiceImpl implements RedisService {
     
        //導入數據源
        @Resource(name = "redisSearchTemplate")
        private StringRedisTemplate redisSearchTemplate;
     
     
        //新增一條該userid用戶在搜索欄的歷史記錄
        //searchkey 代表輸入的關鍵詞
        @Override
        public int addSearchHistoryByUserId(String userid, String searchkey) {
            String shistory = RedisKeyUtils.getSearchHistoryKey(userid);
            boolean b = redisSearchTemplate.hasKey(shistory);
            if (b) {
                Object hk = redisSearchTemplate.opsForHash().get(shistory, searchkey);
                if (hk != null) {
                    return 1;
                }else{
                    redisSearchTemplate.opsForHash().put(shistory, searchkey, "1");
                }
            }else{
                redisSearchTemplate.opsForHash().put(shistory, searchkey, "1");
            }
            return 1;
        }
     
        //刪除個人歷史數據
        @Override
        public Long delSearchHistoryByUserId(String userid, String searchkey) {
            String shistory = RedisKeyUtils.getSearchHistoryKey(userid);
            return redisSearchTemplate.opsForHash().delete(shistory, searchkey);
        }
     
        //獲取個人歷史數據列表
        @Override
        public List getSearchHistoryByUserId(String userid) {
            List stringList = null;
            String shistory = RedisKeyUtils.getSearchHistoryKey(userid);
            boolean b = redisSearchTemplate.hasKey(shistory);
            if(b){
                Cursor> cursor = redisSearchTemplate.opsForHash().scan(shistory, ScanOptions.NONE);
                while (cursor.hasNext()) {
                    Map.Entry map = cursor.next();
                    String key = map.getKey().toString();
                    stringList.add(key);
                }
                return stringList;
            }
            return null;
        }
     
        //新增一條熱詞搜索記錄,將用戶輸入的熱詞存儲下來
        @Override
        public int incrementScoreByUserId(String searchkey) {
            Long now = System.currentTimeMillis();
            ZSetOperations zSetOperations = redisSearchTemplate.opsForZSet();
            ValueOperations valueOperations = redisSearchTemplate.opsForValue();
            List title = new ArrayList<>();
            title.add(searchkey);
            for (int i = 0, lengh = title.size(); i < lengh; i++) {
                String tle = title.get(i);
                try {
                    if (zSetOperations.score("title", tle) <= 0) {
                        zSetOperations.add("title", tle, 0);
                        valueOperations.set(tle, String.valueOf(now));
                    }
                } catch (Exception e) {
                    zSetOperations.add("title", tle, 0);
                    valueOperations.set(tle, String.valueOf(now));
                }
            }
            return 1;
        }
        
        //根據searchkey搜索其相關最熱的前十名 (如果searchkey為null空,則返回redis存儲的前十最熱詞條)
        @Override
        public List getHotList(String searchkey) {
            String key = searchkey;
            Long now = System.currentTimeMillis();
            List result = new ArrayList<>();
            ZSetOperations zSetOperations = redisSearchTemplate.opsForZSet();
            ValueOperations valueOperations = redisSearchTemplate.opsForValue();
            Set value = zSetOperations.reverseRangeByScore("title", 0, Double.MAX_VALUE);
            //key不為空的時候 推薦相關的最熱前十名
            if(StringUtils.isNotEmpty(searchkey)){
                for (String val : value) {
                    if (StringUtils.containsIgnoreCase(val, key)) {
                        if (result.size() > 9) {//只返回最熱的前十名
                            break;
                        }
                        Long time = Long.valueOf(valueOperations.get(val));
                        if ((now - time) < 2592000000L) {//返回最近一個月的數據
                            result.add(val);
                        } else {//時間超過一個月沒搜索就把這個詞熱度歸0
                            zSetOperations.add("title", val, 0);
                        }
                    }
                }
            }else{
                for (String val : value) {
                    if (result.size() > 9) {//只返回最熱的前十名
                        break;
                    }
                    Long time = Long.valueOf(valueOperations.get(val));
                    if ((now - time) < 2592000000L) {//返回最近一個月的數據
                        result.add(val);
                    } else {//時間超過一個月沒搜索就把這個詞熱度歸0
                        zSetOperations.add("title", val, 0);
                    }
                }
            }
            return result;
        }
     
        //每次點擊給相關詞searchkey熱度 +1
        @Override
        public int incrementScore(String searchkey) {
            String key = searchkey;
            Long now = System.currentTimeMillis();
            ZSetOperations zSetOperations = redisSearchTemplate.opsForZSet();
            ValueOperations valueOperations = redisSearchTemplate.opsForValue();
            zSetOperations.incrementScore("title", key, 1);
            valueOperations.getAndSet(key, String.valueOf(now));
            return 1;
        }
     
     
    }
    

    核心的部分寫完了,剩下的需要你自己將如上方法融入到你自己的代碼中就行了。

    代碼實現過濾不雅文字功能

    在springboot 里面寫一個配置類加上@Configuration注解,在項目啟動的時候加載一下,代碼如下:

    package com.***.***.interceptor;
     
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.ClassPathResource;
    import java.io.*;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map;
    import java.util.Set;
     
     
    //屏蔽敏感詞初始化
    @Configuration
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public class SensitiveWordInit {
        // 字符編碼
        private String ENCODING = "UTF-8";
        // 初始化敏感字庫
        public Map initKeyWord() throws IOException {
            // 讀取敏感詞庫 ,存入Set中
            Set wordSet = readSensitiveWordFile();
            // 將敏感詞庫加入到HashMap中//確定有窮自動機DFA
            return addSensitiveWordToHashMap(wordSet);
        }
     
        // 讀取敏感詞庫 ,存入HashMap中
        private Set readSensitiveWordFile() throws IOException {
        Set wordSet = null;
            ClassPathResource classPathResource = new ClassPathResource("static/censorword.txt");
            InputStream inputStream = classPathResource.getInputStream();
            //敏感詞庫
            try {
            // 讀取文件輸入流
                InputStreamReader read = new InputStreamReader(inputStream, ENCODING);
                // 文件是否是文件 和 是否存在
                wordSet = new HashSet();
                // StringBuffer sb = new StringBuffer();
                // BufferedReader是包裝類,先把字符讀到緩存里,到緩存滿了,再讀入內存,提高了讀的效率。
                BufferedReader br = new BufferedReader(read);
                String txt = null;
                // 讀取文件,將文件內容放入到set中
                while ((txt = br.readLine()) != null) {
                    wordSet.add(txt);
                }
                br.close();
                // 關閉文件流
                read.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return wordSet;
        }
        // 將HashSet中的敏感詞,存入HashMap中
        private Map addSensitiveWordToHashMap(Set wordSet) {
        // 初始化敏感詞容器,減少擴容操作
        Map wordMap = new HashMap(wordSet.size());
            for (String word : wordSet) {
                Map nowMap = wordMap;
                for (int i = 0; i < word.length(); i++) {
                    // 轉換成char型
                    char keyChar = word.charAt(i);
                    // 獲取
                    Object tempMap = nowMap.get(keyChar);
                    // 如果存在該key,直接賦值
                    if (tempMap != null) {
                        nowMap = (Map) tempMap;
                    }
                    // 不存在則,則構建一個map,同時將isEnd設置為0,因為他不是最后一個
                    else {
                        // 設置標志位
                        Map newMap = new HashMap();
                        newMap.put("isEnd", "0");
                        // 添加到集合
                        nowMap.put(keyChar, newMap);
                        nowMap = newMap;
                    }
                    // 最后一個
                    if (i == word.length() - 1) {
                        nowMap.put("isEnd", "1");
                    }
                }
            }
            return wordMap;
        }
    }
    

    然后這是工具類代碼 :

    package com.***.***.interceptor;
     
    import java.io.IOException;
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
     
    //敏感詞過濾器:利用DFA算法  進行敏感詞過濾
    public class SensitiveFilter {
        //敏感詞過濾器:利用DFA算法  進行敏感詞過濾
        private Map sensitiveWordMap = null;
     
        // 最小匹配規則
        public static int minMatchType = 1;
     
        // 最大匹配規則
        public static int maxMatchType = 2;
     
        // 單例
        private static SensitiveFilter instance = null;
     
        // 構造函數,初始化敏感詞庫
        private SensitiveFilter() throws IOException {
            sensitiveWordMap = new SensitiveWordInit().initKeyWord();
        }
     
        // 獲取單例
        public static SensitiveFilter getInstance() throws IOException {
            if (null == instance) {
                instance = new SensitiveFilter();
            }
            return instance;
        }
     
        // 獲取文字中的敏感詞
        public Set getSensitiveWord(String txt, int matchType) {
            Set sensitiveWordList = new HashSet();
            for (int i = 0; i < txt.length(); i++) {
                // 判斷是否包含敏感字符
                int length = CheckSensitiveWord(txt, i, matchType);
                // 存在,加入list中
                if (length > 0) {
                    sensitiveWordList.add(txt.substring(i, i + length));
                    // 減1的原因,是因為for會自增
                    i = i + length - 1;
                }
            }
            return sensitiveWordList;
        }
        // 替換敏感字字符
        public String replaceSensitiveWord(String txt, int matchType,
                                           String replaceChar) {
            String resultTxt = txt;
            // 獲取所有的敏感詞
            Set set = getSensitiveWord(txt, matchType);
            Iterator iterator = set.iterator();
            String word = null;
            String replaceString = null;
            while (iterator.hasNext()) {
                word = iterator.next();
                replaceString = getReplaceChars(replaceChar, word.length());
                resultTxt = resultTxt.replaceAll(word, replaceString);
            }
            return resultTxt;
        }
     
        /**
         * 獲取替換字符串
         *
         * @param replaceChar
         * @param length
         * @return
         */
        private String getReplaceChars(String replaceChar, int length) {
            String resultReplace = replaceChar;
            for (int i = 1; i < length; i++) {
                resultReplace += replaceChar;
            }
            return resultReplace;
        }
     
        /**
         * 檢查文字中是否包含敏感字符,檢查規則如下:
    
         * 如果存在,則返回敏感詞字符的長度,不存在返回0
         * @param txt
         * @param beginIndex
         * @param matchType
         * @return
         */
        public int CheckSensitiveWord(String txt, int beginIndex, int matchType) {
            // 敏感詞結束標識位:用于敏感詞只有1位的情況
            boolean flag = false;
            // 匹配標識數默認為0
            int matchFlag = 0;
            Map nowMap = sensitiveWordMap;
            for (int i = beginIndex; i < txt.length(); i++) {
                char word = txt.charAt(i);
                // 獲取指定key
                nowMap = (Map) nowMap.get(word);
                // 存在,則判斷是否為最后一個
                if (nowMap != null) {
                    // 找到相應key,匹配標識+1
                    matchFlag++;
                    // 如果為最后一個匹配規則,結束循環,返回匹配標識數
                    if ("1".equals(nowMap.get("isEnd"))) {
                        // 結束標志位為true
                        flag = true;
                        // 最小規則,直接返回,最大規則還需繼續查找
                        if (SensitiveFilter.minMatchType == matchType) {
                            break;
                        }
                    }
                }
                // 不存在,直接返回
                else {
                    break;
                }
            }
     
            if (SensitiveFilter.maxMatchType == matchType){
                if(matchFlag < 2 || !flag){        //長度必須大于等于1,為詞
                    matchFlag = 0;
                }
            }
            if (SensitiveFilter.minMatchType == matchType){
                if(matchFlag < 2 && !flag){        //長度必須大于等于1,為詞
                    matchFlag = 0;
                }
            }
            return matchFlag;
        }
    }
    

    在你代碼的controller層直接調用方法判斷即可:

    //非法敏感詞匯判斷
    SensitiveFilter filter = SensitiveFilter.getInstance();
    int n = filter.CheckSensitiveWord(searchkey,0,1);
    if(n > 0){ //存在非法字符
        logger.info("這個人輸入了非法字符--> {},不知道他到底要查什么~ userid--> {}",searchkey,userid);
        return null;
    }
    

    也可將敏感文字替換*等字符 :

    SensitiveFilter filter = SensitiveFilter.getInstance();
    String text = "敏感文字";
    String x = filter.replaceSensitiveWord(text, 1, "*");
    

    最后剛才的 SensitiveWordInit.java 里面用到了 censorword.text 文件,放到你項目里面的 resources 目錄下的 static 目錄中,這個文件就是不雅文字大全,也需要您與時俱進的更新,項目啟動的時候會加載該文件。

    stringredis
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    魔改版內網掃描工具
    2023-05-11 14:38:25
    XScan使用文檔前言這是一個縫合怪● go-crack ● fscan?快速上手默認掃描?./xscan -hf ip.txt -finger -vulnscan -xscan 360 -t 100. ./xscan -h 192.168.123.22/24,192.168.123.1-255,192.168.122.1-192.168.123.254 -finger -vulnscan -xscan 360 -t 100 -userfile user.txt -passfile pass.txt. Spy模塊進入大內網以后,支持探測指定網段存活./xscan -h 192.168.123.22/24,192.168.123.1-255,192.168.122.1-192.168.123.254 -finger -vulnscan -xscan 360 -t 100 -m Spy. HTTP模塊./Xscan-Mac -m http -addr 0.0.0.0:6666默認密碼 qax qax
    提及 Redis 自然是耳熟能詳,說起 Redis 的漏洞的話,未授權訪問漏洞、主從復制漏洞等也是張口就來,所以打算對 Redis 進行一個全面的總結。
    代表的a的二進制位的修改。對應的ASCII碼是97,轉換為二進制數據是01100001. 因為bit非常節省空間,可以用來做大數據量的統計。BITOPNOTdestkeykey ,對給定 key 求邏輯非,并將結果保存到 destkey 。獲取今天點擊最多的15條:zrevrange hotNews:20190926 0 15 withscores
    Redis系列漏洞總結
    2023-06-19 10:29:18
    前言Redis的未授權漏洞一直都是一個很火的漏洞,最近看許多前輩的文章自己復現后,根據自己的實踐再次總結一下,為日后復習方便回顧。Redis簡介redis是一個key-value存儲系統。和Memcached類似,它支持存儲的value類型相對更多,包括string、list、set、zset和hash。這些數據類型都支持push/pop、add/remove及取交集并集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支持各種不同方式的排序。
    本文主要介紹在使用阿里云 Redis 的開發規范,從下面幾個方面進行說明。鍵值設計命令使用客戶端使用相關工具通過本文的介紹可以減少使用 Redis 過程帶來的問題。不要包含特殊字符反例:包含空格、換行、單雙引號以及其他轉義字符2、value 設計拒絕 bigkey防止網卡流量、慢查詢,string 類型控制在 10KB 以內,hash、list、set、zset 元素個數不要超過 5000。
    寫在前面需求是做一個秒殺系統,比如大家來搶100臺手機,先到先得。查閱了網上很多用redis實現秒殺的demo,竟然沒一個能用的!!!有些是php的,沒閑心研究了,現在說說為什么不能用:絕大多數的DEMO都是基于redis的watch特性的事務實現①,個別是基于redis分布式鎖實現②。當然還有些用了腳本的,我也沒仔細看是lua還是調用redis指令,哪有那個閑心去研究哇。并且使用這種方式實現呢,在并發量較大的時候,過多的重試線程應該會嚴重影響服務器性能。
    使用java和redis實現一個簡單的熱搜功能,具備以下功能: 搜索欄展示當前登陸的個人用戶的搜索歷史記錄,刪除個人歷史記錄 用戶在搜索欄輸入某字符,則將該字符記錄下來 以zset格式存儲的redis中,記錄該字符被搜索的個數以及當前的時間戳 (用了DFA算法,感興趣的自己百度學習吧) 每當用戶查詢了已在redis存在了的字符時,則直接累加個數, 用來獲取平臺上最熱查詢的十條數據。(可以自己寫接
    面對越來越多的高并發場景,限流顯示的尤為重要。 當然,限流有許多種實現的方式,Redis具有很強大的功能,我用Redis實踐了三種的實現方式,可以較為簡單的實現其方式。Redis不僅僅是可以做限流,還可以做數據統計,附近的人等功能,這些可能會后續寫到。
    Redis 是一個開源的使用 ANSI C 語言編寫、支持網絡、可基于內存亦可持
    解決方案使用mysql數據庫,使用一個字段來存儲庫存,每次扣減庫存去更新這個字段。將庫存放到redis使用redis的incrby特性來扣減庫存。基于數據庫單庫存第一種方式在所有請求都會在這里等待鎖,獲取鎖有去扣減庫存。基于數據庫多庫存第二種方式其實是第一種方式的優化版本,在一定程度上提高了并發量,但是在還是會大量的對數據庫做更新操作大量占用數據庫資源。但是一旦緩存丟失需要考慮恢復方案。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类