本文目录导读:

我来为您详细说明PHP API签名验证的完整流程。
签名验证的基本原理
核心思想
通过特定的算法将请求参数、密钥和时间戳等信息生成签名,服务端验证签名确保请求的合法性和完整性。
标准签名验证流程
客户端签名生成流程
<?php
/**
* 客户端签名生成
*/
class ApiSignClient {
private $appKey;
private $appSecret;
public function __construct($appKey, $appSecret) {
$this->appKey = $appKey;
$this->appSecret = $appSecret;
}
/**
* 生成签名
* @param array $params 业务参数(不包含签名)
* @param string $timestamp 时间戳
* @param string $nonce 随机字符串
* @return string
*/
public function generateSign($params, $timestamp, $nonce) {
// 1. 加入公共参数
$params['app_key'] = $this->appKey;
$params['timestamp'] = $timestamp;
$params['nonce'] = $nonce;
// 2. 按key排序
ksort($params);
// 3. 拼接字符串
$str = urldecode(http_build_query($params));
// 4. 加入密钥并加密
$sign = md5($str . $this->appSecret);
return $sign;
}
/**
* 生成请求参数
*/
public function createRequestParams($businessParams) {
$timestamp = time();
$nonce = $this->generateNonce(16);
$params = array_merge($businessParams, [
'app_key' => $this->appKey,
'timestamp' => $timestamp,
'nonce' => $nonce
]);
$params['sign'] = $this->generateSign($businessParams, $timestamp, $nonce);
return $params;
}
/**
* 生成随机字符串
*/
private function generateNonce($length = 16) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$nonce = '';
for ($i = 0; $i < $length; $i++) {
$nonce .= $chars[rand(0, strlen($chars) - 1)];
}
return $nonce;
}
}
服务端签名验证流程
<?php
/**
* 服务端签名验证
*/
class ApiSignServer {
private $appKey;
private $appSecret;
private $timeout; // 请求超时时间(秒)
public function __construct($appKey, $appSecret, $timeout = 300) {
$this->appKey = $appKey;
$this->appSecret = $appSecret;
$this->timeout = $timeout;
}
/**
* 验证签名主方法
* @param array $params 请求参数
* @return bool|string 成功返回true,失败返回错误信息
*/
public function verifySign($params) {
try {
// 1. 检查必填参数
$this->checkRequiredParams($params);
// 2. 验证AppKey
if ($params['app_key'] !== $this->appKey) {
return 'Invalid app_key';
}
// 3. 验证时间戳
if (!$this->checkTimestamp($params['timestamp'])) {
return 'Timestamp expired';
}
// 4. 验证Nonce(防止重放攻击)
if (!$this->checkNonce($params['nonce'])) {
return 'Nonce repeated';
}
// 5. 重新生成签名并对比
$sign = $this->generateSign($params);
if ($sign !== $params['sign']) {
return 'Sign verification failed';
}
// 6. 记录nonce(需要在红缓存或数据库中实现)
$this->saveNonce($params['nonce'], $this->timeout);
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
/**
* 检查必填参数
*/
private function checkRequiredParams($params) {
$required = ['app_key', 'timestamp', 'nonce', 'sign'];
foreach ($required as $field) {
if (!isset($params[$field]) || empty($params[$field])) {
throw new Exception("Missing required parameter: {$field}");
}
}
// 检查业务参数是否完整(根据具体业务)
// ...
}
/**
* 验证时间戳是否过期
*/
private function checkTimestamp($timestamp) {
$currentTime = time();
if ($timestamp < $currentTime - $this->timeout || $timestamp > $currentTime + $this->timeout) {
return false;
}
return true;
}
/**
* 检查Nonce是否重复
*/
private function checkNonce($nonce) {
// 实现方式1:Redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
if ($redis->exists("api_nonce:{$nonce}")) {
return false;
}
return true;
// 实现方式2:数据库
// $query = "SELECT COUNT(*) FROM api_nonce WHERE nonce = ?";
// if (查询结果 > 0) return false;
// return true;
}
/**
* 保存Nonce(设置过期时间)
*/
private function saveNonce($nonce, $timeout) {
// Redis实现
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->setex("api_nonce:{$nonce}", $timeout, time());
// 数据库实现
// INSERT INTO api_nonce (nonce, created_at) VALUES (?, NOW())
// 定期清理过期记录
}
/**
* 生成服务端签名
*/
private function generateSign($params) {
// 去除签名参数
unset($params['sign']);
// 按key排序
ksort($params);
// 拼接字符串
$str = urldecode(http_build_query($params));
// 加入密钥并加密
return md5($str . $this->appSecret);
}
}
// 使用示例
$apiSignServer = new ApiSignServer('your_app_key', 'your_app_secret');
$verifyResult = $apiSignServer->verifySign($_GET);
if ($verifyResult === true) {
// 签名验证通过,处理业务逻辑
echo json_encode(['code' => 200, 'message' => 'Success']);
} else {
// 签名验证失败
echo json_encode(['code' => 401, 'message' => $verifyResult]);
}
高级签名方案(更安全)
使用HMAC-SHA256签名
<?php
/**
* 高级签名生成(使用HMAC-SHA256)
*/
class AdvancedApiSign {
/**
* 生成签名
*/
public static function generateSign($params, $appSecret) {
// 1. 过滤空值和签名字段
$params = array_filter($params, function($value) {
return $value !== '' && $value !== null && $value !== 'sign';
});
// 2. 按key排序
ksort($params);
// 3. 拼接字符串 "key1=value1&key2=value2"
$str = http_build_query($params);
// 4. 使用HMAC-SHA256加密
$sign = hash_hmac('sha256', $str, $appSecret);
return strtoupper($sign);
}
/**
* 验证签名
*/
public static function verifySign($params, $appSecret) {
if (!isset($params['sign'])) {
return false;
}
$clientSign = $params['sign'];
unset($params['sign']);
$serverSign = self::generateSign($params, $appSecret);
// 使用恒等比较防止时序攻击
return hash_equals($serverSign, $clientSign);
}
}
更安全的签名流程(包含请求体签名)
<?php
/**
* 完整的安全签名方案
*/
class SecureApiSign {
private $appKey;
private $appSecret;
private $timestamp;
private $nonce;
/**
* 完整签名流程
*/
public function signRequest($method, $path, $body = null, $timestamp = null) {
$timestamp = $timestamp ?? time();
$nonce = $this->generateNonce(32);
$params = [
'app_key' => $this->appKey,
'timestamp' => $timestamp,
'nonce' => $nonce,
'method' => strtoupper($method),
'path' => $path
];
// 如果有请求体,加入签名
if ($body !== null) {
$params['body'] = sha1($body);
}
// 生成签名
ksort($params);
$str = urldecode(http_build_query($params));
$sign = hash_hmac('sha256', $str, $this->appSecret);
return [
'params' => $params,
'sign' => $sign
];
}
/**
* 验证请求
*/
public function verifyRequest($request) {
try {
// 1. 验证时间戳(5分钟内有效)
if (abs(time() - $request['timestamp']) > 300) {
throw new Exception('Request expired');
}
// 2. 验证Nonce
if (!$this->verifyNonce($request['nonce'])) {
throw new Exception('Nonce already used');
}
// 3. 验证签名
$signParams = $request;
unset($signParams['sign']);
ksort($signParams);
$str = urldecode(http_build_query($signParams));
$expectedSign = hash_hmac('sha256', $str, $this->appSecret);
if (!hash_equals($expectedSign, $request['sign'])) {
throw new Exception('Invalid signature');
}
// 4. 验证请求体
if (isset($request['body'])) {
$actualBodyHash = sha1(file_get_contents('php://input'));
if ($actualBodyHash !== $request['body']) {
throw new Exception('Body verification failed');
}
}
return true;
} catch (Exception $e) {
error_log("Signature verification failed: " . $e->getMessage());
return false;
}
}
private function generateNonce($length) {
return bin2hex(random_bytes($length / 2));
}
}
中间件实现(Laravel示例)
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class VerifyApiSignature
{
public function handle(Request $request, Closure $next)
{
$signature = $request->header('X-Signature');
$timestamp = $request->header('X-Timestamp');
$nonce = $request->header('X-Nonce');
if (!$signature || !$timestamp || !$nonce) {
return response()->json(['error' => 'Missing signature parameters'], 401);
}
// 获取API密钥
$apiKey = $request->header('X-Api-Key');
$appSecret = $this->getSecretByKey($apiKey);
if (!$appSecret) {
return response()->json(['error' => 'Invalid API key'], 401);
}
// 验证时间戳
if (abs(time() - $timestamp) > 300) {
return response()->json(['error' => 'Request expired'], 401);
}
// 验证Nonce(Redis实现)
if (Redis::exists("nonce:{$nonce}")) {
return response()->json(['error' => 'Nonce repeated'], 401);
}
// 重新生成签名
$data = $request->all();
$data['timestamp'] = $timestamp;
$data['nonce'] = $nonce;
ksort($data);
$str = http_build_query($data);
$expectedSignature = hash_hmac('sha256', $str, $appSecret);
if (!hash_equals($expectedSignature, $signature)) {
return response()->json(['error' => 'Invalid signature'], 401);
}
// 保存Nonce
Redis::setex("nonce:{$nonce}", 300, time());
return $next($request);
}
private function getSecretByKey($apiKey)
{
// 从数据库或配置中获取密钥
return 'your_app_secret';
}
}
安全建议
签名算法选择
- 简单应用:MD5(但不够安全)
- 生产环境:HMAC-SHA256
- 高安全性:RSA非对称加密
防攻击措施
- 时间限制:签名有有效期(建议5-10分钟)
- 防重放:使用Nonce,一次请求只能使用一次
- 请求限制:IP限流、用户限流
密钥管理
- 定期更换密钥
- 密钥分级管理(不同级别使用不同密钥)
- 密钥加密存储
日志记录
- 记录所有签名验证失败请求
- 记录异常行为用于安全分析
完整集成示例
<?php
/**
* API请求类(客户端)
*/
class ApiClient {
private $baseUrl;
private $appKey;
private $appSecret;
public function __construct($baseUrl, $appKey, $appSecret) {
$this->baseUrl = $baseUrl;
$this->appKey = $appKey;
$this->appSecret = $appSecret;
}
public function request($method, $path, $params = []) {
$timestamp = time();
$nonce = bin2hex(random_bytes(16));
// 构造签名数据
$signData = array_merge([
'app_key' => $this->appKey,
'timestamp' => $timestamp,
'nonce' => $nonce,
'method' => strtoupper($method),
'path' => $path
], $params);
// 生成签名
ksort($signData);
$str = urldecode(http_build_query($signData));
$sign = hash_hmac('sha256', $str, $this->appSecret);
// 发送请求
$headers = [
'X-Api-Key: ' . $this->appKey,
'X-Timestamp: ' . $timestamp,
'X-Nonce: ' . $nonce,
'X-Signature: ' . $sign,
'Content-Type: application/json'
];
// 使用curl发送请求
$ch = curl_init($this->baseUrl . $path);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if (strtoupper($method) === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
}
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
}
一个完整的API签名验证流程通常包含:
- 客户端负责生成签名(参数排序、拼接、加密)
- 服务端验证签名(检查时间戳、Nonce、重新生成签名比对)
- 安全防护(防重放攻击、防篡改、密钥管理等)
选择合适的签名算法和实现方案能显著提升API的安全性。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播