一文搞懂接口参数签名与验签

随着前后端分离的开发模式的发展,接口安全显得越来越重要。我们通常的做法是为接口的请求参数进行加密,也就是我们常说的“签名与验签”。希望本篇笔记能帮助部分需要的小伙伴们。

一文搞懂接口参数签名与验签

说在前面:安全的方法不能局限于此!!!本篇笔记仅供参考学习。

签名步骤:

既然要签名(加密),那么我们一定要有自己的加密方法。以下为示例的加密步骤。

  1. 将所有的参数进行字典排序;
  2. 将排序后的数组key与value用“&”符号连接。生成类似Get请求的参数格式。如:tel=15588888888&passwd=123456×tamp=165031031
  3. 将第二步生成的字符串参数进行MD5加密;
  4. 将第三步生成的签名转换为大写;

签名方法(PHP版)

  1. /**
  2.  * Sign签名方法
  3.  * @param array $param 待签名的参数
  4.  * @param string $key 请求方Key
  5.  * @return string 签名
  6.  * @throws \think\Exception
  7.  */
  8. function generateToken(array $param,string $key=''):string{
  9.     if(emptyempty($param)) throw new \think\Exception('参数不能为空');
  10.     ksort($param);
  11.     if (isset($param['sign'])) unset($param['sign']);
  12.     foreach ($param as $k => $v) {
  13.         if ('' === $v || null === $vcontinue;
  14.         $buff .= "{$k}={$v}&";
  15.     }
  16.     $buff .= ("key=" . $key);
  17.     return strtoupper(md5($buff));
  18. }

参数说明:

  • $param 请求主体参数,数组形式,不包含Key;
  • $key 为了安全起见建议将Key放在请求的Header部分,具体键值双方约定即可,常见的是放入Hrader部分的AuthorizationApp-Key等;

签名验证

签名的验证非常简单,只需一行代码判断请求体中的Sign签名与签名结果是否一致即可。示例如下:

  1. var_dump($param['sign'] == generateToken($param,$key));
  2. //返回布尔类型true或false

其他语言拓展:

1、Java版签名加密方法

  1. import java.util.Map;
  2. import java.util.TreeMap;
  3. import java.security.MessageDigest;
  4. import java.security.NoSuchAlgorithmException;
  5. public class SignUtil {
  6.   /**
  7.    * Sign签名方法
  8.    *
  9.    * @param param 待签名的参数
  10.    * @param key 请求方Key
  11.    * @return 签名
  12.    * @throws Exception
  13.    */
  14.   public static String generateToken(Map<String, String> param, String key) throws Exception {
  15.     if (param.isEmpty()) {
  16.       throw new Exception("参数不能为空");
  17.     }
  18.     // 将参数按照ASCII码从小到大排序
  19.     Map<String, String> sortedParam = new TreeMap<>(param);
  20.     // 拼接参数
  21.     StringBuilder sb = new StringBuilder();
  22.     for (Map.Entry<String, String> entry : sortedParam.entrySet()) {
  23.       String k = entry.getKey();
  24.       String v = entry.getValue();
  25.       if (v != null && !v.equals("")) {
  26.         sb.append(k).append("=").append(v).append("&");
  27.       }
  28.     }
  29.     sb.append("key=").append(key);
  30.     // 计算MD5签名
  31.     return getMD5(sb.toString()).toUpperCase();
  32.   }
  33.   /**
  34.    * 计算MD5签名
  35.    *
  36.    * @param str 待签名的原始字符串
  37.    * @return MD5签名
  38.    * @throws NoSuchAlgorithmException
  39.    */
  40.   private static String getMD5(String str) throws NoSuchAlgorithmException {
  41.     MessageDigest md = MessageDigest.getInstance("MD5");
  42.     byte[] bytes = md.digest(str.getBytes());
  43.     StringBuilder sb = new StringBuilder();
  44.     for (byte b : bytes) {
  45.       String hex = Integer.toHexString(b & 0xff);
  46.       if (hex.length() == 1) {
  47.         sb.append("0");
  48.       }
  49.       sb.append(hex);
  50.     }
  51.     return sb.toString();
  52.   }
  53. }

2、Python版签名加密方法

  1. import hashlib
  2. def generateToken(param: dict, key: str) -> str:
  3.     if not param:
  4.         raise Exception('参数不能为空')
  5.     # 将参数按照ASCII码从小到大排序
  6.     sortedParam = dict(sorted(param.items()))
  7.     # 拼接参数
  8.     buff = ''
  9.     for k, v in sortedParam.items():
  10.         if v is not None and v != '':
  11.             buff += f'{k}={v}&'
  12.     buff += f'key={key}'
  13.     # 计算MD5签名
  14.     return hashlib.md5(buff.encode('utf-8')).hexdigest().upper()

 

你想把广告放到这里吗?

发表评论

您必须 登录 才能发表留言!