防止第三方请求接口方案
方案:临时token(对称加密)
该方法需要前端与后端共同约定一个令牌参数,发出请求前结合 token 参数和随机数,生成临时的随机 token(有效期60秒或更多),通过请求的header携带该参数,由后端使用相同的加密手段得到与约定 token 匹配的参数。
在每次发起请求的header中携带 timestamp(时间戳)、nonce(随机字符串)、sign(加密后的token),由后端校验sign的一致性。
举例:
前端(JavaScript)
// 创建一个 Date 对象var date = new Date();
// 设置时区为 "Asia/Shanghai"
var date = new Date();
date.toLocaleString("en-US", {timeZone: "Asia/Shanghai"});
let timestamp = date.getTime();
const NEKOT = 'TESTTOKEN';
const query = {
timestamp: timestamp, // '1704285994000'
nonce: 'bbkknn', // 此处为每次请求接口时随机生成的任意长度字符串
token: NEKOT // 此处为与后端约定好的TOKEN
};
let filteredQuery = Object.fromEntries(
Object.entries(query).filter(([key, value]) => value.length > 0)
);
let sortedQuery = Object.fromEntries(
Object.entries(filteredQuery).sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
);
let queryString = new URLSearchParams(sortedQuery).toString();
let sign = md5(queryString); // 此处为生成的临时token
后端(PHP(此处为ThinkPHP的中间件))
以PHP为例,其他语言逻辑一样,只需转换为对应语法即可。
<?php
namespace app\index\middleware;
use think\exception\HttpResponseException;
use think\response\Json as JsonResponse;
// 在访问接口时需经过的中间件
class Base
{
protected $TOKEN = 'TESTTOKEN'; // 与前端约定好的TOKEN
const EXPIRE_TIME = 60; // 临时token的过期时间
public function handle($request, \Closure $next)
{
// 前端生成sign时的时间戳
$timestamp = $request->header("Request-TimeStamp");
// 前端生成的临时token
$signature = $request->header("Signature");
// 前端自己生成的随机字符串
$nonce = $request->header("Nonce");
$now = time();
if (abs(floor($timestamp / 1000) - $now) > self::EXPIRE_TIME) {
// 过期的请求、抛出异常
}
$query = [
'timestamp' => $timestamp,
'nonce' => $nonce,
'token' => $this->TOKEN
];
$query = array_filter($query, 'strlen');
ksort($query);
$sign = md5(http_build_query($query)); // 与前端对称加密后的sign
if ($sign !== $signature) {
// 非法请求、抛出400
}
return $next($request); // 校验通过,执行下一步操作
}
}
优点:
校验逻辑少,响应快,拦截效果立竿见影。
缺点:
请求具有响应时效性(比如60秒),一般是够用的 无大碍。 由于token需要储存在前端,不能百分百保证不被三方破解,这点可在前端的加密部分提升代码复杂度以提升安全性。前端通过VUE打包后只要没有源码一般很难解译出token