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

需求:

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

思路:

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

  1. 獲取Request的加密數(shù)據(jù),進(jìn)行解密。
  2. 將解密之后的數(shù)據(jù),傳入Request。
  3. 必須要在Spring MVC處理之前。

實現(xiàn)步驟:

  1. 修改Request 的Paramter

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

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

但Request是沒有提供類似于setParamter(String name, Object value)的方法的,所以, 我的插入點,就是通過重寫Request的三個獲取參數(shù)的主要方法,來達(dá)到修改參數(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的包裝類, 進(jìn)去一看,也果然如此。


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

這個時候, 一切需要的準(zhǔn)備就緒, 開始寫我們自定義的Request吧

public class ParameterRequestWrapper extends HttpServletRequestWrapper {

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

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

    //查找自定的Map進(jìn)行返回
    @Override
    public String getParameter(String name) {
        Object v = params.get(name);
        if (v == null) {
            return null;
        } else if (v instanceof String[]) { //一個name可能對應(yīng)多個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服務(wù)器管理的所有web資源:例如Jsp, Servlet, 靜態(tài)圖片文件或靜態(tài) html 文件等進(jìn)行攔截,從而實現(xiàn)一些特殊的功能。例如實現(xiàn)URL級別的權(quán)限訪問控制、過濾敏感詞匯、壓縮響應(yīng)信息等一些高級功能。
它主要用于對用戶請求進(jìn)行預(yù)處理,也可以對HttpServletResponse進(jìn)行后處理。使用Filter的完整流程:Filter對用戶請求進(jìn)行預(yù)處理,接著將請求交給Servlet進(jìn)行處理并生成響應(yīng),最后Filter再對服務(wù)器響應(yīng)進(jìn)行后處理。

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

找到著手點, 就可以開始寫啦。

首先, 約定好請求參數(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ù)進(jìn)行加密, content是原有的數(shù)據(jù)以name=value&name1=value2的形式進(jìn)行拼接,通過加密得到的數(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");

            //是否加密, 是則進(jìn)行解密
            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進(jìn)行解密
                        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("&"); //通過&進(jìn)行分割

        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ù)進(jìn)行解密。
  3. 將解密之后的參數(shù)放到篡改之后的request。
  4. 使用篡改之后的Request執(zhí)行下一個Filter

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容