1 Star 2 Fork 1

码农兴哥/rx-php-box

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
HelloApiAuth.php 5.34 KB
一键复制 编辑 原始数据 按行查看 历史
<?php
namespace app\middleware\api;
use app\common\utils\ApiReturn;
use app\common\utils\Init;
use app\common\utils\MyLog;
use app\common\utils\RedisUtils;
use think\Request;
use Closure;
class HelloApiAuth
{
/**
* 处理请求
* @param Request $request
* @param Closure $next
*/
public function handle(Request $request, Closure $next)
{
try {
//在中间件设置唯一的请求ID,和后续的日志中的请求ID保持一致
Init::setRequestId();
// 1. 获取必要请求头
$accessKey = $request->header('X-Access-Key');
$timestamp = $request->header('X-Timestamp');
$nonce = $request->header('X-Nonce');
$signature = $request->header('X-Signature');
$contentMd5 = $request->header('Content-MD5');
$isEncrypted = $request->header('X-Encrypted') === 'true';
MyLog::writeLog($request->header(), 'HelloApiAuth_handle_header');
// 校验必须的头部
if (empty($accessKey) || empty($timestamp) || empty($nonce) || empty($signature)) {
return $this->error(400, 'Missing required headers');
}
// 2. 校验时间戳(防重放)
$now = time();
$tolerance = config('hello_api.timestamp_tolerance');
if (abs($now - $timestamp) > $tolerance) {
return $this->error(403, 'Timestamp expired');
}
// 3. 校验 Nonce(防重放,缓存有效期设为 10 分钟)
$nonceKey = 'api_nonce_' . $accessKey . '_' . $nonce;
if (RedisUtils::init()->get($nonceKey)) {
return $this->error(403, 'Nonce already used');
}
RedisUtils::init()->set($nonceKey, 1, 600);
// 4. 获取原始请求体
$rawBody = $request->getContent();
// 计算 BodyMD5(若请求头提供则校验,否则只用于签名)
$bodyMd5 = md5($rawBody);
if (!empty($contentMd5) && $contentMd5 !== $bodyMd5) {
return $this->error(422, 'Content-MD5 mismatch');
}
// 5. 根据 AccessKey 获取 SecretKey
$access_key = config('hello_api.client_access_key');
if (empty($access_key)) {
return $this->error(401, '当前系统的 ACCESS_KEY 未配置');
}
if ($accessKey !== $access_key) {
return $this->error(401, 'Invalid AccessKey');
}
$secret_key = config('hello_api.client_secret_key');
if (empty($secret_key)) {
return $this->error(401, '当前系统的 SECRET_KEY 未配置');
}
// 6. 构造待签名字符串并验证签名
$signParams = [
'AccessKey' => $accessKey,
'BodyMD5' => $bodyMd5,
'Nonce' => $nonce,
'Secret' => $secret_key,
'Timestamp' => $timestamp,
];
ksort($signParams);
$signStr = http_build_query($signParams, '', '&');
$expectedSignature = hash_hmac('sha256', $signStr, $secret_key);
if (!hash_equals($expectedSignature, $signature)) {
return $this->error(401, 'Signature verification failed');
}
// 7. 如果开启了加密,则解密请求体
if ($isEncrypted && config('hello_api.enable_encryption', false)) {
// 假设密文结构为:Base64( IV(12字节) + TAG(16字节) + 密文 )
$decoded = base64_decode($rawBody);
if (strlen($decoded) < 28) {
return $this->error(400, 'Invalid encrypted data');
}
$iv = substr($decoded, 0, 12);
$tag = substr($decoded, 12, 16);
$ciphertext = substr($decoded, 28);
$plaintext = openssl_decrypt($ciphertext, 'aes-256-gcm', $secret_key, OPENSSL_RAW_DATA, $iv, $tag);
if ($plaintext === false) {
return $this->error(422, 'Decryption failed');
}
// 将解密后的明文替换原始请求体
$rawBody = $plaintext;
// 可选:重新校验解密后的 JSON 格式
if (json_decode($rawBody) === null) {
return $this->error(422, 'Invalid JSON after decryption');
}
}
// ===== 这两行代码我暂时用不到了,因为现在只是鉴权,不加密。鉴权通过后,我直接在controller层 $params = $request->post(); 获取到的 $params 就是对方传来的请求参数了。
// 8. 将解析后的参数存入 Request,供控制器使用
//$request->HelloApiAuth = (json_decode($rawBody, true) ?: []);
//MyLog::writeLog($request->HelloApiAuth, 'request->HelloApiAuth');
// 继续执行控制器
return $next($request);
} catch (\Exception|\Error $e) {
MyLog::writeExceptionLog($e, 'HelloApiAuth_handle_exception');
ApiReturn::retError($e);
}
}
/**
* 统一错误响应
*/
protected function error($code, $message)
{
return json([
'code' => $code,
'msg' => $message,
'data' => null,
])->code($code);
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/rxbook/rx-php-box.git
git@gitee.com:rxbook/rx-php-box.git
rxbook
rx-php-box
rx-php-box
master

搜索帮助