修改Request對象的Paramter。---對請求數(shù)據(jù)進行解密

需求:

為了數(shù)據(jù)安全食铐, 前端對傳過來的數(shù)據(jù)進行了加密匕垫, 后端這邊為了在不修改原有接口的情況下,需要在業(yè)務層(Controller)之前做解密虐呻,并能在業(yè)務層拿到需要的參數(shù)象泵。

思路:

眾所周知, Request是不允許修改參數(shù)的斟叼,這個估計和數(shù)據(jù)安全性有關(guān)偶惠。但是因為后端使用的Spring MVC 到Controller那層時候, 是獲取Request的參數(shù)朗涩, 進行自動封裝的忽孽, 所以,為了不大規(guī)模的修改原有接口谢床, 只能在Srping MVC在提取Request的參數(shù)之前兄一, 對數(shù)據(jù)進行修改。所以大概的要點有:

  1. 獲取Request的加密數(shù)據(jù)识腿,進行解密出革。
  2. 將解密之后的數(shù)據(jù),傳入Request渡讼。
  3. 必須要在Spring MVC處理之前骂束。

實現(xiàn)步驟:

  1. 修改Request 的Paramter

想來大家都知道, 獲取request的參數(shù)成箫, 無非就是關(guān)鍵的幾個方法

  • getParameter(String name) 獲取name對應的value展箱, 如果有多個返回第一個
  • getParameterNames()獲取request里面所有的name,返回一個Enumeration類型
  • getParameterValues(String name)獲取name對應的所有value

但Request是沒有提供類似于setParamter(String name, Object value)的方法的蹬昌,所以混驰, 我的插入點,就是通過重寫Request的三個獲取參數(shù)的主要方法凳厢,來達到修改參數(shù)的目的账胧。

找到插入點之后, 就是開始實現(xiàn)HttpServletRequest先紫, 可是你會發(fā)現(xiàn)你要實現(xiàn)的方法如此之多治泥。。遮精。

HttpServletRequest需要實現(xiàn)的一部分方法

全部實現(xiàn)一遍確實不是可好主意...但通過HttpServletRequest的實現(xiàn)結(jié)構(gòu)里居夹,發(fā)現(xiàn)了這么一個類:HttpServletRequestWrapper

查看實現(xiàn)或繼承了HttpServletRequest的類

看名字就知道败潦, 這就是HttpServletRequest的包裝類, 進去一看准脂,也果然如此劫扒。


HttpServletRequest的構(gòu)造函數(shù), 一定需要個request作為參數(shù)進行包裝
默認使用Request的獲取參數(shù)方法

這個時候狸膏, 一切需要的準備就緒沟饥, 開始寫我們自定義的Request吧

public class ParameterRequestWrapper extends HttpServletRequestWrapper {

    private Map<String,String[]> params;//定義參數(shù)集合

    //需要一個request和 篡改之后的參數(shù)進行實例化。
    public ParameterRequestWrapper(HttpServletRequest request, Map<String,String[]> newParams) {
        super(request);
        this.params = newParams;
    }

    //查找自定的Map進行返回
    @Override
    public String getParameter(String name) {
        Object v = params.get(name);
        if (v == null) {
            return null;
        } else if (v instanceof String[]) { //一個name可能對應多個value湾戳, 返回第一個
            String[] strArr = (String[]) v;
            if (strArr.length > 0) {
                return strArr[0]; 
            } else {
                return null;
            }
        } else if (v instanceof String) {
            return (String) v;
        } else {
            return v.toString();
        }
    }

    @Override
    public Map getParameterMap() {
        return params;
    }

    @Override
    public Enumeration getParameterNames() {
        return new Vector(params.keySet()).elements();
    }

    @Override
    public String[] getParameterValues(String name) {
        Object v = params.get(name);
        if (v == null) {
            return null;
        } else if (v instanceof String[]) {
            return (String[]) v;
        } else if (v instanceof String) {
            return new String[] { (String) v };
        } else {
            return new String[] { v.toString() };
        }
    }

}
  1. 解密參數(shù)贤旷, 使用解密后的參數(shù)創(chuàng)建一個自定義的Request。

在做這一步的時候砾脑, 要考慮一個問題就是幼驶,如何把修改后的Request供Spring MVC去使用?Spring MVC的處理是從DispatcherServlet開始的韧衣,通過查看Servlet文檔可以發(fā)現(xiàn)盅藻,在執(zhí)行Servlet的時候, 會先執(zhí)行Filter畅铭。

Filter也稱之為過濾器氏淑,它是Servlet技術(shù)中最實用的技術(shù),Web開發(fā)人員通過Filter技術(shù)顶瞒,對web服務器管理的所有web資源:例如Jsp, Servlet, 靜態(tài)圖片文件或靜態(tài) html 文件等進行攔截夸政,從而實現(xiàn)一些特殊的功能。例如實現(xiàn)URL級別的權(quán)限訪問控制榴徐、過濾敏感詞匯、壓縮響應信息等一些高級功能匀归。
它主要用于對用戶請求進行預處理坑资,也可以對HttpServletResponse進行后處理。使用Filter的完整流程:Filter對用戶請求進行預處理穆端,接著將請求交給Servlet進行處理并生成響應袱贮,最后Filter再對服務器響應進行后處理。

通過描述可以看到体啰,F(xiàn)ilter可以在處理Servlet之前進行Request進行預處理攒巍, 嗯, 這就是我們想要的了~

找到著手點荒勇, 就可以開始寫啦柒莉。

首先, 約定好請求參數(shù)形式(POST請求)

isEnc: Y
content: u1oKuV89GRAaA5/ZCnLUChHAkLfNZq+l/GBkKD4h5G3izv64fABV10ITVFZORRzt+BPgs7ym+adG
ObnHhQAr3aoxa1LDKAsZc/Bn8/iW3m2CdHni+moj2D5sTWFpa0Zu9wAUp2qYiKU6DG0hhZ/gAoVb
t+vYekZfzsvQpU+mEhlggF2GPpva519VPTB2I5O/aTlGcuThjVQEkqhhe6jjR3qi77t0R5jVHVXo
ZLv/94hcm5WUKLxS03TOz4nGyjbE+RzWSlt36/5oYJS+pjmSwo8iKNpcBUYox/PUT4cUVJCQZzSD
jGOpu75H+mG1H4OZ

這里通過isEnc來約定是否對數(shù)據(jù)進行加密沽翔, content是原有的數(shù)據(jù)以name=value&name1=value2的形式進行拼接兢孝,通過加密得到的數(shù)據(jù)窿凤。具體的加密方式以及數(shù)據(jù)約定方式根據(jù)需求自己定義

  • 編寫自己的Filter, 可以實現(xiàn)Spring的Filter跨蟹,也可以實現(xiàn)javax.servlet.Filter雳殊, 在這里我實現(xiàn)了Spring的OncePerRequestFilter
@WebFilter({"/*"}) //攔截所有的請求
public class DecodeFilter extends OncePerRequestFilter {

    private static final Logger logger = LoggerFactory.getLogger(DecodeFilter.class);

    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException {

            String isEnc = req.getParameter("isEnc");

            //是否加密, 是則進行解密
            if (org.apache.commons.lang3.StringUtils.isNotEmpty(isEnc) && isEnc.equals("Y")) {

                try {

                    //獲取request的請求數(shù)據(jù)
                    String bodyInfoEn = req.getParameter("content");
                    
                    if (org.apache.commons.lang3.StringUtils.isNotEmpty(bodyInfoEn)) {
                        logger.info("參數(shù)解密前: {}", bodyInfoEn);

                        String bodyInfoDe = DecodeTool.decrypt(bodyInfoEn);//對content進行解密
                        logger.info("url: {}窗轩, 解密后: {}", req.getRequestURI(), bodyInfoDe);

                        if (org.apache.commons.lang3.StringUtils.isNotEmpty(bodyInfoDe)) {
                            //解析body里面的參數(shù)與uri頭里面的參數(shù)信息
                            Map<String, String[]> parametersMap = getParamterMap(req, bodyInfoDe);

                            //由于request請求沒有修改參數(shù)的權(quán)限夯秃,使用篡改后的request代替原先的
                            ParameterRequestWrapper requestWrapper  = new ParameterRequestWrapper(req, parametersMap);

                            chain.doFilter(requestWrapper, res);
                            return;
                        }
                    }

                    //如果content不存在則返回錯誤信息

                } catch (Exception e) {
                    logger.error("請求參數(shù)解密失敗...", e);
                }

            } else { //normal
                chain.doFilter(req, res);
            }

    }

     /**
     * 獲取解析好的參數(shù)信息
     *
     * @param req
     * @param bodyInfoDe
     * @return Map<String,String[]>
     */
    private Map<String,String[]> getParamterMap(HttpServletRequest req, String bodyInfoDe) throws UnsupportedEncodingException {

        Map<String, String[]> parametersMap = new HashMap<String, String[]>(20);
        String[] paramFlex = bodyInfoDe.split("&"); //通過&進行分割

        for (String s : paramFlex) {
            String[] pairparam = s.split("=");
            if (pairparam.length > 1) {
                parametersMap.put(pairparam[0], new String[]{pairparam[1]});
            }
        }

        return parametersMap;
    }
}

至此, 這個解密Filter就開發(fā)完成了痢艺。整個Filter完成的事情主要是

  1. 判斷是否加密仓洼,不是加密則直接執(zhí)行下一個Filter
  2. 加密則根據(jù)約定好的參數(shù)進行解密。
  3. 將解密之后的參數(shù)放到篡改之后的request腹备。
  4. 使用篡改之后的Request執(zhí)行下一個Filter

最后衬潦,整個請求數(shù)據(jù)解密的功能就已經(jīng)實現(xiàn),如果需要將返回數(shù)據(jù)進行加密的同學植酥,請參考我下一篇文章《對返回數(shù)據(jù)進行自定義加密》~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末镀岛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子友驮,更是在濱河造成了極大的恐慌漂羊,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卸留,死亡現(xiàn)場離奇詭異走越,居然都是意外死亡,警方通過查閱死者的電腦和手機耻瑟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門旨指,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人喳整,你說我怎么就攤上這事谆构。” “怎么了框都?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵搬素,是天一觀的道長。 經(jīng)常有香客問我魏保,道長熬尺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任谓罗,我火速辦了婚禮粱哼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妥衣。我一直安慰自己皂吮,他們只是感情好戒傻,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蜂筹,像睡著了一般需纳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上艺挪,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天不翩,我揣著相機與錄音,去河邊找鬼麻裳。 笑死口蝠,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的津坑。 我是一名探鬼主播妙蔗,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼疆瑰!你這毒婦竟也來了眉反?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤穆役,失蹤者是張志新(化名)和其女友劉穎寸五,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耿币,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡梳杏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了淹接。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片十性。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖塑悼,靈堂內(nèi)的尸體忽然破棺而出烁试,到底是詐尸還是另有隱情,我是刑警寧澤拢肆,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站靖诗,受9級特大地震影響郭怪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刊橘,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一鄙才、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧促绵,春花似錦攒庵、人聲如沸嘴纺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽栽渴。三九已至,卻和暖如春稳懒,著一層夾襖步出監(jiān)牢的瞬間闲擦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工场梆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留墅冷,地道東北人。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓或油,卻偏偏與公主長得像寞忿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子顶岸,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350