因为前后端分离,所以接口必须暴露在公网环境,某些恶意用户会通过浏览器控制台查看接口,然后使用HTTP请求工具调用接口,因为请求工具可以伪造请求头等信息,通过请求头判断是否为恶意请求难度比较大,所以要对参数进行签名。
前端参数签名操作方法
// 1. 传给后端的参数对象,增加时间戳参数
let data = {id: 1, name: 11, channel_id:1, timestamp: 123456789};
// 2. 获取data的key,排序规则按照第一个字符的ASCII码值递增排序
let keys = Object.keys(data).sort()
// 3. 将参数按照一定的规则拼接成字符串
let buff = '';
keys.forEach((key) => {
buff += key + '=' + data[key] + '&'
})
buff = buff.substr(0, buff.length - 1)
// 4. 加密拼接好的字符串
signature = encrypt(buff);
// 5. 将密文传给后端
data.signature = signature;
/**
* 加密方法
* @param buff
*/
function encrypt(buff) {
// 进入签名加密操作
return buff;
}
后端签名校验方法
/**
* 前端参数加密,防开挂校验,防止篡改参数,防止重复调用
* @param array $params 前端传的所有参数
* @throws BusinessException
*/
public static function checkSignature(array $params) {
if (!self::isNotEmpty($params, BasicConstant::SIGNATURE))
throw new BusinessException('参数错误,缺少签名');
Log::info('前端参数,排序前: ' . json_encode($params));
// 1. 参数排序
ksort($params);
Log::info('排序后: ' . json_encode($params));
// 2. 将参数拼接成字符串,除了 signature 签名之外都用 key=value& 的方式拼接
$buff = '';
foreach ($params as $k => $v) {
if ($k != BasicConstant::SIGNATURE) {
$buff .= $k . "=" . $v . "&";
}
}
// 去除最后一个多余的 &
$buff = trim($buff, "&");
Log::info('拼接后: ' . $buff);
// 3. 将签名放入 redis,如果 redis 中存在此签名,说明重复请求
$encrypt = self::desEncrypt($buff);
Log::info('加密后的密文: ' . $encrypt);
$exists = RedisService::setExNx($encrypt, true, 10);
if (!$exists)
throw new BusinessException("非法请求,重复调用");
// 4. 解密校验参数
$decrypt = self::desDecrypt($params[BasicConstant::SIGNATURE]);
Log::info('解密:' . $decrypt);
// 如果签名解密后与接收到的实际参数不一致,说明参数被篡改
if ($buff != $decrypt)
throw new BusinessException('非法请求,参数可能被篡改');
$decryptParams = self::convertUrlQuery($decrypt);
Log::info('解密后解析成数组:' . json_encode($decryptParams));
// 判断是否超时
if (time() - $decryptParams['timestamp'] > 10)
throw new BusinessException('签名超时');
}
/**
* 自定义解密方法
* @param $string
* @return string
*/
public static function desEncrypt($string)
{
return $string;
}
/**
* 自定义加密方法
* @param $string
* @return string
*/
public static function desDecrypt($string)
{
return $string;
}
这样就能实现接口恶意调用的校验了,如果用户拿参数去重复调用的话会提示重复调用,如果用户修改了参数的话会提示参数被篡改。