常見開放接口簽名簡單實現-請求參數排序加密


常見的簽名方式實現一般分為以下幾個步驟 : 

  1 . 將所有(或者特殊)請求參數按特定規則排序;

  2 . 將請求參數按特定規則拼裝為加密字符串;

  3 . 加密算法對加密字符串進行加密,得到簽名。

下面自己寫了一個常見的實現方式,以便記錄,這里只是示例說明基本常規實現,使用則還是根據項目的真實情況去選擇。

  例如,下面我簡單實現了一個restful接口,/signTest去驗證簽名。

  1.我這里使用了MD5加密方式,首先在pom.xml加入依賴。

<dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.10</version>
 </dependency>

  2 . 因為會對請求的參數進行處理,主要是為了獲取到所有的請求參數,我寫了一個自定義的HttpUtils.java : 

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * Created by  EalenXie on 2018/6/13 12:47
 */
public enum HttpUtils {

    getHttpUtil;

    //獲取請求IP
    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            ip = request.getHeader("Proxy-Client-IP");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            ip = request.getHeader("WL-Proxy-Client-IP");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            ip = request.getHeader("HTTP_CLIENT_IP");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            ip = request.getRemoteAddr();
        return ip;
    }

    //將請求參數轉換成Map
    public static Map<String, String> getUrlParams(HttpServletRequest request) {
        String param = "";
        try {
            param = URLDecoder.decode(request.getQueryString(), "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        Map<String, String> result = new HashMap<>();
        String[] params = param.split("&");
        for (String s : params) {
            Integer index = s.indexOf("=");
            result.put(s.substring(0, index), s.substring(index + 1));
        }
        return result;
    }

    //從請求中獲取所有參數
    public static SortedMap<String, String> getAllParams(HttpServletRequest request, Map<String, String> postParams) {
        SortedMap<String, String> result = new TreeMap<>();
        Map<String, String> urlParams = getUrlParams(request);
        for (Map.Entry entry : urlParams.entrySet()) {
            result.put((String) entry.getKey(), (String) entry.getValue());
        }
        if (postParams != null) {
            for (Map.Entry entry : postParams.entrySet()) {
                result.put((String) entry.getKey(), (String) entry.getValue());
            }
        }
        return result;
    }
}

  3 . 簽名工具類,實現簽名機制 SignUtils.java : 

import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.SortedMap;

/**
 * Created by  EalenXie on 2018/6/13 9:31
 */
public enum SignUtil {

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

    /**
     * @param params 所有的請求參數都會在這里進行排序加密
     * @return 得到簽名
     */
    public String getSign(SortedMap<String, String> params) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry entry : params.entrySet()) {
            if (!entry.getKey().equals("sign")) { //拼裝參數,排除sign
                if (!StringUtils.isEmpty(entry.getKey()) && !StringUtils.isEmpty(entry.getValue()))
                    sb.append(entry.getKey()).append(entry.getValue());
            }
        }
        logger.info("Before Sign : {}", sb.toString());
        return DigestUtils.md5Hex(sb.toString()).toUpperCase();
    }

    /**
     * @param params 所有的請求參數都會在這里進行排序加密
     * @return 驗證簽名結果
     */
    public boolean verifySign(SortedMap<String, String> params) {
        if (params == null || StringUtils.isEmpty(params.get("sign"))) return false;
        String sign = getSign(params);
        logger.info("verify Sign : {}", sign);
        return !StringUtils.isEmpty(sign) && params.get("sign").equals(sign);
    }

}

  4 . SendGoodsController.java 提供Rest接口 /signTest。

package com.wuxicloud.web;

import com.wuxicloud.util.HttpUtils;
import com.wuxicloud.util.SignUtil;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.Null;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;

/**
 * Created by  EalenXie on 2018/6/13 11:54
 */
@RestController
@CrossOrigin  //加個注解,解決前端調接口跨域,這里只是為了方便測試,真實情況慎用。
public class SendGoodsController {

    @RequestMapping("/signTest")
    @ResponseBody
    public Map<String, String> sendGood(@RequestBody(required = false) Map<String, String> params, HttpServletRequest request) {
        SortedMap<String, String> allParams = HttpUtils.getAllParams(request, params);
        boolean isSigned = SignUtil.getSignUtil.verifySign(allParams);
        Map<String,String> result= new HashMap<>();
        if (isSigned)  result.put("flag","Signed Success!");
        else result.put("flag","Signed Fail");
        return result;
    }
}

 

 

  5 . 啟動類,此時服務器端的代碼已經准備好了。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Created by  EalenXie on 2018/6/13 11:52
 */
@SpringBootApplication
public class GoodsApplication {
    public static void main(String[] args) {
        SpringApplication.run(GoodsApplication.class, args);
    }
}

 

   6。此時,假設某前端頁面要調用該接口,應該怎么做呢?

  1 . 將請求接口的參數按照與服務器相同規則進行排序;

  2 . 將請求接口的參數按照與服務器相同規則拼裝為加密字符串;

  3 . 與服務器相同的加密算法實現加密,得到簽名;

  針對本例中的簽名方式,我下面寫了一個基本的實現。

  1.因為使用到MD5,到網上去下載了一個md5.min.js(這個js有網上一大把);

  2.下個jquery.min.js;

  3.自己實現的簽名方式 Sign.js : 

/**
 * Created by EalenXie on 2018/6/13 15:11
 */

/**
 * @param url 請求的url,應該包含請求參數(url的?后面的參數)
 * @param requestParams 請求參數(POST的JSON參數)
 * @returns {string} 獲取簽名
 */
function getSign(url, requestParams) {
    var signString = "";
    var urlParams = parseQueryString(url);
    var requestBody = sortObject(mergeObject(urlParams, requestParams));
    for (var i in requestBody) {
        signString += i + requestBody[i];
    }
    return md5.hex(signString).toUpperCase();
}

/**
 * @param url 請求的url
 * @returns {{}} 將url中請求參數組裝成json對象(url的?后面的參數)
 */
function parseQueryString(url) {
    var urlReg = /^[^\?]+\?([\w\W]+)$/,
        paramReg = /([^&=]+)=([\w\W]*?)(&|$|#)/g,
        urlArray = urlReg.exec(url),
        result = {};
    if (urlArray && urlArray[1]) {
        var paramString = urlArray[1], paramResult;
        while ((paramResult = paramReg.exec(paramString)) != null) {
            result[paramResult[1]] = paramResult[2];
        }
    }
    return result;
}
/**
 * @param object 傳入要進行屬性排序的對象
 * @returns {{}} 將對象進行屬性排序(按首字母順序進行排序)
 */
function sortObject(object) {
    var objectKeys = Object.keys(object).sort();
    var result = {};
    for (var i in objectKeys) {
        result[objectKeys[i]] = object[objectKeys[i]];
    }
    return result;
}

/**
 * @returns {*} 將兩個對象合並成一個
 */
function mergeObject(objectOne, objectTwo) {
    if (objectTwo != null) {
        for (var key in objectTwo) {
            objectOne[key] = objectTwo[key]
        }
    }
    return objectOne;
}

  進行測試的test.html : 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="jquery.min.js"></script>
    <script src="md5.min.js"></script>
    <script src="sign.js"></script>
    <script>
        var url = "localhost:8080/signTest?time=2018-06-13 15:43";
        var requestParam = {"username": "EalenXie", "password": "admin", "gender": "", "age": 23};
        function test() {
            var sign = getSign(url, requestParam);  //根據Url和請求參數得到簽名
            var requestUrl = url += "&sign=" + sign;  //將簽名添加在請求參數后面去請求接口
            $.ajax({
                type: 'POST',       //GET請求效果相同
                headers: {
                    'Access-Control-Allow-Origin': "*",
                    "content-type": "application/json",
                    "Accept": "application/json"
                },
                async: false,
                url: requestUrl,
                data: JSON.stringify(requestParam),
                dataType: 'json',
                success: function (response) {
                    alert(response.flag);
                }
            });

        }
    </script>
    <title>Title</title>
</head>
<body style="align-content: center">
<button onclick="test()">點擊測試</button>
</body>
</html>

  7.此時,啟動后端項目。再打開test.html點擊進行測試。

  

  8.點擊發現,得到服務器返回的驗證簽名調用成功的響應了。

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM