Web項目聚集地
圖文教程,技術交流
如圖,是我們模擬的一個從瀏覽器發送給服務器端的轉賬請求。久一的ID是 web_resource,正在操作100元的轉賬。
再如圖,因爲是通過瀏覽器 `url` 訪問服務,這個時候金額被篡改成了 200,那麼服務器接受到了200,直接扣除了200怎麼解決?這就是本文要講解的內容。
防止url被篡改的方式有很多種,本文就講述最簡單的一種,通過 secret 加密驗證。
道理很簡單,服務器接收到了 price 和 id,如果有辦法校驗一下他們是否被修改過不就就可以了嗎?
那麼我們傳遞的時候增加一個參數,叫做sign,sign是使用用戶不可見的一個secret和price、id組合加密獲得,然後傳遞給服務器端。當服務器端接收到請求的時候,獲取到price、id,通過同樣的secret加密和sign比較如果相同就通過校驗,不同則被篡改過。
那麼問題來了,如果參數特別多怎麼辦?
所以通用的做法是,把所有需要防止篡改的參數按照字母正序排序,然後順序拼接到一起,再和secret組合加密得到 sign。具體的做法可以參照如下。
public static String generateSign(Map<String, String> parameters) { try { List<String> names = new ArrayList<>(); parameters.forEach((k, v) -> { if (v != null && !Objects.equals(v, "") && !Objects.equals(k, "sign")) { names.add(k); } }); List sortedNames = names.stream().sorted() .collect(Collectors.toList()); StringBuffer sb = new StringBuffer(); sortedNames.forEach(n -> sb.append(String.format("%s=%s", n, parameters.get(n)))); String sign = md5(sb.toString()); return sign; } catch (Exception e) { return ""; } } private static String md5(String inputString) throws NoSuchAlgorithmException, UnsupportedEncodingException { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(inputString.getBytes("UTF-8")); byte[] digest = md.digest(); return convertByteToHex(digest); } private static String convertByteToHex(byte[] byteData) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < byteData.length; i++) { sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16) .substring(1)); } return sb.toString(); }
generateSign 就是所有需要加密的參數,包括secret
有的同學擔心,那麼他萬一猜到了我的加密算法怎麼辦,這個不用擔心,你的secret是保持在服務器端的,不會暴漏出去的,所以他知道了算法也不會知道具體加密的內容。
那麼問題又來了,如果小明通過抓包工具獲取到了URL,他是不是可以無限制的訪問這個地址呢?那就出現了“久一”的錢被一百一百的轉空了。
那可怎麼辦?這裏涉及到了另一個話題,接口的冪等,我們後面會詳細講解怎麼通過冪等控制重複扣款。這裏我們要講解的是怎麼控制 URL 失效。
這裏又有一個通用的做法,就是再添加一個參數 timestamp。對的,就是當前的時間戳。服務器獲取到 timestamp 以後檢驗一下是否在5分鐘以內,如果不是直接返回請求失效就可以了?那麼如果timestamp 被篡改了呢?不會的,因爲我們按照上面的做法同樣對 timestamp 做了加密防止篡改。
最簡單的校驗接口被篡改的方式,你學會了嗎?