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

常見的簽名方式實現通常分爲如下幾個步驟 : html

  1 . 將全部(或者特殊)請求參數按特定規則排序;前端

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

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

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

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

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

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

  2 . 由於會對請求的參數進行處理,主要是爲了獲取到全部的請求參數,我寫了一個自定義的HttpUtils.java : spring

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 : apache

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。json

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.點擊發現,獲得服務器返回的驗證簽名調用成功的響應了。

  

相關文章
相關標籤/搜索