method()) !== 'GET') { return $this->fail('仅支持 GET 请求', ReturnCode::EMPTY_PARAMS); } $param = $request->get(); $signature = trim((string) ($param['signature'] ?? '')); $secret = trim((string) ($param['secret'] ?? '')); $device = trim((string) ($param['device'] ?? '')); $time = trim((string) ($param['time'] ?? '')); if ($signature === '' || $secret === '' || $device === '' || $time === '') { return $this->fail('signature、secret、device、time 均为必传且不能为空', ReturnCode::EMPTY_PARAMS); } $serverSecret = trim((string) config('api.auth_token_secret', '')); if ($serverSecret === '') { return $this->fail('服务未配置 API_AUTH_TOKEN_SECRET', ReturnCode::EMPTY_PARAMS); } if ($secret !== $serverSecret) { return $this->fail('密钥错误', ReturnCode::EMPTY_PARAMS); } $tolerance = (int) config('api.auth_token_time_tolerance', 300); $now = time(); $ts = is_numeric($time) ? (int) $time : 0; if ($ts <= 0 || abs($now - $ts) > $tolerance) { return $this->fail('时间戳无效或已过期', ReturnCode::EMPTY_PARAMS); } $sign = $this->getAuthToken($device, $serverSecret, $time); if ($sign !== $signature) { return $this->fail('签名验证失败', ReturnCode::EMPTY_PARAMS); } $exp = (int) config('api.auth_token_exp', 86400); $tokenResult = JwtToken::generateToken([ 'id' => 0, 'plat' => 'api', 'device' => $device, 'access_exp' => $exp, ]); // 同一设备只保留最新 token,覆盖后旧 token 失效 AuthTokenCache::setDeviceToken($device, $tokenResult['access_token'], $exp); return $this->success([ 'auth-token' => $tokenResult['access_token'], 'expires_in' => $tokenResult['expires_in'], ]); } /** * 生成签名:signature = md5(device . secret . time) * * @param string $device 设备标识 * @param string $secret 密钥(来自配置) * @param string $time 时间戳 * @return string */ private function getAuthToken(string $device, string $secret, string $time): string { return md5($device . $secret . $time); } }