1.对接平台接口新增api-key参数

This commit is contained in:
2026-05-25 09:31:24 +08:00
parent 9a43e1d8f2
commit cde5a851e5
10 changed files with 105 additions and 11 deletions

View File

@@ -49,10 +49,10 @@
## 2. 鉴权与对接流程(平台侧 /api/v1
平台侧接口分两步:
平台侧接口需统一携带请求头 **`api-key`**(与服务端 `.env``API_KEY` 一致),业务接口另需 **`auth-token`**。
1. **获取 `auth-token`**
2. **携带 `auth-token` 调用 `/api/v1/*` 业务接口**
1. **获取 `auth-token`**(同时携带 `api-key`
2. **携带 `api-key` + `auth-token` 调用 `/api/v1/*` 业务接口**
### 2.1 获取 auth-token
@@ -100,11 +100,22 @@ signature = md5(agent_id + secret + time)
- 密钥错误/签名错误/时间戳无效:`code=403`
- 服务端未配置密钥或生成失败:`code=500`
### 2.2 调用 v1 业务接口(携带 auth-token
### 2.2 平台 api-key所有 /api/v1/* 必填
`/api/v1/authToken` 外,其余 `/api/v1/*` 接口需要在请求头携带:
- **取值**:与服务端环境变量 `API_KEY` 完全一致(部署在 `server/.env`
- **适用范围**:所有 `/api/v1/*` 接口(含 `/api/v1/authToken` 与业务接口)
- **携带方式**(任选其一,按优先级读取,先命中即采用):
1. 请求头 `api-key: <API_KEY>`**推荐**
2. URL 查询参数 `api_key=<API_KEY>`(或 `api-key=<API_KEY>`
3. body 表单/JSON 字段 `api_key`(或 `api-key`
- **未携带或错误**`401` / `403`
- `auth-token: <authtoken>`
### 2.3 调用 v1 业务接口(携带 auth-token
`/api/v1/authToken` 外,其余 `/api/v1/*` 接口需要携带:
- `api-key: <与 API_KEY 一致>`(请求头 / query / body 任选其一,参见 2.2
- `auth-token: <authtoken>`(仅支持请求头)
`auth-token` 过期或失效,返回 `code=402`,需要重新调用 `/api/v1/authToken` 获取新 token。

View File

@@ -4,7 +4,7 @@ DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=dafuweng-v3
DB_USER=dafuweng-v3
DB_PASSWORD=tA6rciKLKxpFNGAm
DB_PASSWORD=123456
DB_PREFIX=
DB_POOL_MAX=32
DB_POOL_MIN=4
@@ -28,14 +28,16 @@ WEBMAN_CHANNEL_LISTEN_HOST=0.0.0.0
GAME_URL=dice-v3-game.h55555game.top
# API 鉴权与用户(可选,不填则用默认值)
# 平台对接 /api/v1/* 请求头 api-key必填与对接方约定
API_KEY=
# authToken 签名密钥(必填,与客户端约定,用于 signature 校验)
API_AUTH_TOKEN_SECRET=xF75oK91TQj13s0UmNIr1NBWMWGfflNO
API_AUTH_TOKEN_SECRET=
# authToken 时间戳允许误差秒数,防重放,默认 300
API_AUTH_TOKEN_TIME_TOLERANCE=300
API_AUTH_TOKEN_EXP=86400
# API_USER_TOKEN_EXP=604800
API_USER_CACHE_EXPIRE=86400
API_USER_ENCRYPT_KEY=Wj818SK8dhKBKNOY3PUTmZfhQDMCXEZi
API_USER_ENCRYPT_KEY=
# 验证码配置,支持cache|session
CAPTCHA_MODE=cache

View File

@@ -48,6 +48,9 @@ return [
'没有原因' => 'Unknown reason',
'缺少参数agent_id、secret、time、signature 不能为空' => 'Missing parameters: agent_id, secret, time, signature are required',
'服务端未配置 API_AUTH_TOKEN_SECRET' => 'API_AUTH_TOKEN_SECRET is not configured',
'服务端未配置 API_KEY' => 'API_KEY is not configured',
'请携带 api-key' => 'Please provide api-key',
'api-key 无效' => 'Invalid api-key',
'密钥错误' => 'Invalid secret',
'时间戳已过期或无效,请同步时间' => 'Timestamp expired or invalid, please sync time',
'签名验证失败' => 'Signature verification failed',

View File

@@ -20,6 +20,7 @@ class ApiAccessLogMiddleware implements MiddlewareInterface
/** 请求头名称(小写) */
private const SENSITIVE_HEADER_NAMES = [
'api-key',
'auth-token',
'token',
'authorization',
@@ -32,6 +33,8 @@ class ApiAccessLogMiddleware implements MiddlewareInterface
'secret',
'signature',
'token',
'api-key',
'api_key',
'auth-token',
'auth_token',
'old_token',

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace app\api\middleware;
use app\api\util\ReturnCode;
use plugin\saiadmin\exception\ApiException;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
/**
* 校验对接平台 api-key与 .env 中 API_KEY 一致)
* 仅用于 /api/v1/* 平台对接接口
*
* 取值优先级(按顺序读取,首个非空即采用):
* 1. 请求头 api-key推荐
* 2. 查询参数 api_key / api-key
* 3. body 表单/JSON api_key / api-key
*/
class ApiKeyMiddleware implements MiddlewareInterface
{
public function process(Request $request, callable $handler): Response
{
$expected = (string) config('api.platform_api_key', '');
if ($expected === '') {
throw new ApiException('API_KEY is not configured', ReturnCode::SERVER_ERROR);
}
$apiKey = $this->resolveApiKey($request);
if ($apiKey === '') {
throw new ApiException('Please provide api-key', ReturnCode::UNAUTHORIZED);
}
if (!hash_equals($expected, $apiKey)) {
throw new ApiException('Invalid api-key', ReturnCode::FORBIDDEN);
}
return $handler($request);
}
private function resolveApiKey(Request $request): string
{
$headerValue = $request->header('api-key');
if ($headerValue !== null && trim((string) $headerValue) !== '') {
return trim((string) $headerValue);
}
foreach (['api_key', 'api-key'] as $key) {
$val = $request->get($key);
if ($val !== null && trim((string) $val) !== '') {
return trim((string) $val);
}
}
foreach (['api_key', 'api-key'] as $key) {
$val = $request->post($key);
if ($val !== null && trim((string) $val) !== '') {
return trim((string) $val);
}
}
return '';
}
}

View File

@@ -11,6 +11,8 @@ return [
'session_username_prefix' => env('API_SESSION_USERNAME_PREFIX', 'api:user:session:'),
// 登录会话过期时间(秒),默认 7 天
'session_expire' => (int) env('API_SESSION_EXPIRE', 604800),
// 平台对接请求头 api-key/api/v1/* 必填,与客户端请求头 api-key 一致)
'platform_api_key' => env('API_KEY', ''),
// auth-token 签名密钥(与客户端约定,用于 /api/authToken 的 signature 校验,必填)
'auth_token_secret' => env('API_AUTH_TOKEN_SECRET', ''),
// auth-token 时间戳允许误差(秒),防重放,默认 300 秒

View File

@@ -14,6 +14,7 @@
use Webman\Route;
use app\api\middleware\ApiAccessLogMiddleware;
use app\api\middleware\ApiKeyMiddleware;
use app\api\middleware\AuthTokenMiddleware;
use app\api\middleware\TokenMiddleware;
@@ -22,9 +23,10 @@ Route::group('/api/v1', function () {
Route::any('/authToken', [app\api\controller\v1\AuthTokenController::class, 'index']);
})->middleware([
ApiAccessLogMiddleware::class,
ApiKeyMiddleware::class,
]);
// 平台 v1 接口:需在请求头携带 auth-token
// 平台 v1 接口:需在请求头携带 api-key、auth-token
Route::group('/api/v1', function () {
Route::any('/getGameList', [app\api\controller\v1\GameController::class, 'getGameList']);
Route::any('/getGameHall', [app\api\controller\v1\GameController::class, 'getGameHall']);
@@ -36,6 +38,7 @@ Route::group('/api/v1', function () {
Route::any('/setPlayerWallet', [app\api\controller\v1\GameController::class, 'setPlayerWallet']);
})->middleware([
ApiAccessLogMiddleware::class,
ApiKeyMiddleware::class,
AuthTokenMiddleware::class,
]);

View File

@@ -4,6 +4,9 @@ declare(strict_types=1);
return [
'ACCOUNT_DISABLED' => 'Account is disabled and cannot log in',
'API_AUTH_TOKEN_SECRET is not configured' => 'API_AUTH_TOKEN_SECRET is not configured',
'API_KEY is not configured' => 'API_KEY is not configured',
'Please provide api-key' => 'Please provide api-key',
'Invalid api-key' => 'Invalid api-key',
'AUTH_TOKEN_EXPIRED' => 'auth-token expired',
'AUTH_TOKEN_FORMAT_INVALID' => 'auth-token format invalid',
'AUTH_TOKEN_INVALID' => 'auth-token invalid',

View File

@@ -4,6 +4,9 @@ declare(strict_types=1);
return [
'ACCOUNT_DISABLED' => '账号已被禁用,无法登录',
'API_AUTH_TOKEN_SECRET is not configured' => '服务端未配置 API_AUTH_TOKEN_SECRET',
'API_KEY is not configured' => '服务端未配置 API_KEY',
'Please provide api-key' => '请携带 api-key',
'Invalid api-key' => 'api-key 无效',
'AUTH_TOKEN_EXPIRED' => 'auth-token 已过期',
'AUTH_TOKEN_FORMAT_INVALID' => 'auth-token 格式无效',
'AUTH_TOKEN_INVALID' => 'auth-token 无效',

View File

@@ -11,7 +11,7 @@ declare(strict_types=1);
$options = getopt('', ['agent_id:', 'secret:', 'time::']);
$agentId = $options['agent_id'] ?? '5ef059938ba799aaa845e1c2e8a762bd';
$agentId = $options['agent_id'] ?? '202cb962ac59075b964b07152d234b70';
$secret = $options['secret'] ?? 'xF75oK91TQj13s0UmNIr1NBWMWGfflNO';
$time = $options['time'] ?? (string) time();