当前位置:首页>php>php接口完全-签名校验

php接口完全-签名校验

  • 2026-10-07 09:47:41
php接口完全-签名校验

一、签名验证的意义

签名验证用于确保数据在传输过程中未被篡改,常见于API请求和消息传递。通过生成签名(哈希值),接收方可用相同算法和密钥重新计算并校验签名,从而保证数据的完整性和真实性。

二、服务端(PHP)签名生成流程

签名生成步骤如下:
  • 移除原有签名字段:unset($data['sign']);
  • 参数字典序排序:ksort($data);
  • 拼接参数字符串: 使用 http_build_query 将所有参数按 参数=参数值 形式用 & 连接。
  • 追加密钥:在参数字符串后加上 key。
  • 哈希加密:采用 sha256 算法加密。
  • 转为大写:最终签名结果全部转为大写。
PHP示例代码:
functionsign_create($data, $key){    unset($data['sign']);       ksort($data);    $string =  http_build_query($data, '', '&');    $string = $string.'&key='.$key;    $string = hash('sha256', $string);    return mb_strtoupper($string);}functionsign_verify($data, $key){    if(empty($data['sign'])) {        throw new ValidateException('缺少sign参数');    }    if(!isset($data['timestamp'])) {        throw new ValidateException('缺少timestamp参数');    }    if(!isset($data['nonce']) || 32 != \strlen($data['nonce'])) {        throw new ValidateException('缺少随机字符串或长度不符合要求');    }    if(abs(time() * 1000 - $data['timestamp']) > 60000) {        throw new ValidateException('签名已过期');    }    if(self::create($data, $key) != $data['sign']) {        throw new ValidateException('签名不一致');    }    return true;}$get = $request->get();$post = $request->post();$data = Arr::only($request->header(), ['sign', 'timestamp', 'nonce']);$data['token'] = $request->token();$data['params'] = http_build_query($get);$data['data'] = http_build_query($post);$data['platform'] = $request->platform();sign_create($data, $secret);

三、前端(Vue)签名生成流程

前端通过当前请求时间、随机字符串、GET/POST参数、登录token、加密密钥等关键参数参与签名验证,防止数据被篡改。
主要流程:
  • 生成随机字符串和时间戳
  • 获取token及平台标识
  • 构建请求参数
  • 排序参数并拼接
  • 追加密钥,进行哈希加密(SHA256),转为大写
  • 返回签名及相关参数
JavaScript示例代码:
import CryptoJS from 'crypto-js'import qs from 'qs'export function createSign(data) {  const token = getToken() || ''  const timestamp = Math.floor(Date.now())  const nonce = randomStr(32)  const key = '加密秘钥,于服务端约定好规则'  const platform = 'pcadmin'  data.token = token  data.nonce = nonce  data.timestamp = timestamp  data.platform = platform  data = ksort(data)  const signStr = httpBuildQuery(data) + '&key=' + key  const sign = CryptoJS.SHA256(signStr).toString().toUpperCase()  return {    sign,    nonce,    timestamp,    platform  }}

常用工具函数

  • httpBuildQuery:适配PHP的http_build_query,使用qs库实现序列化。
  • randomStr:生成指定长度的随机字符串。
  • buildHeaderData:根据请求参数构建签名和请求头。
  • ksort:模拟 PHP 的 ksort 函数:按对象/数组的键名升序排序
相关代码:
export function httpBuildQuery(data) {  return qs.stringify(data, {    format: 'RFC1738'  })}export function randomStr(length) {  let result = ''  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'  for (let i = 0; i < length; i++) {    const randomIndex = Math.floor(Math.random() * characters.length)    result += characters.charAt(randomIndex)  }  return result}export function buildHeaderData(params = {}, data = {}) {  const signData = {    params: httpBuildQuery(params || {}),    data: httpBuildQuery(data || {})  }  return createSign(signData)}/** * 模拟 PHP 的 ksort 函数:按对象/数组的键名升序排序 * @param {Object|Array} obj 要排序的对象/数组 * @param {boolean} [preserveNumericKeys=true] 是否保留数字键的原始类型(true=保留,false=转为字符串) * @returns {Object} 按键名升序排序后的新对象 */export function ksort(obj, preserveNumericKeys = true) {  // 1. 提取键值对并处理键名类型(区分数字/字符串)  const entries = Object.entries(obj).map(([key, value]) => {    // 判断是否为纯数字键(如 '123' → 123,'123a' → 保留字符串)    const isNumericKey = /^\d+$/.test(key)    const processedKey = isNumericKey ? Number(key) : key    return { key: processedKey, originalKey: key, value }  })  // 2. 按键名排序(核心逻辑)  entries.sort((a, b) => {    const keyA = a.key    const keyB = b.key    // 数字键 vs 数字键:按数值大小排序    if (typeof keyA === 'number' && typeof keyB === 'number') {      return keyA - keyB    }    // 数字键 vs 字符串键:数字键在前    if (typeof keyA === 'number') return -1    if (typeof keyB === 'number') return 1    // 字符串键 vs 字符串键:按 UTF-16 编码升序(兼容 PHP 的字符排序)    return keyA.localeCompare(keyB, 'en-US', { sensitivity: 'base' })  })  // 3. 重构排序后的对象(保留原键名)  const sortedObj = {}  entries.forEach(({ originalKey, value }) => {    // 可选:是否将数字键转回字符串(如 1 → '1',与原生对象键名格式一致)    const finalKey = preserveNumericKeys ? originalKey : String(originalKey)    sortedObj[finalKey] = value  })  return sortedObj}

四、依赖说明

qs:用于解析和序列化查询字符串,保证与PHP兼容。
npm install qs
crypto-js:流行的JavaScript加密库,支持多种加密算法(如SHA256)。
npm install crypto-js

总结

  • 服务端和前端需约定一致的签名规则和密钥。
  • 参数排序、拼接方式需统一,保证签名结果一致。
  • 推荐采用SHA256等安全算法,并将结果统一为大写,提升安全性和兼容性。

最新文章

随机文章