Sign(签名) 是接口验证安全性的一种常用的技术,在一定程度上,可以保证我们数据接口得安全性, Sign一般都需要配合加密算法来使用,常用的AES系列算法(对称算法)、SHA系列算法(安全散列算法)、RSA系列算法(非对称算法)等,我这边现在使用的AES对称加密算法。
AES加密和解密示例代码
namespace app\common\lib;
class Aes{
public $key = '';
public $iv = '';
/**
* 构造方法
*/
public function __construct($config){
foreach($config as $k=>$v){
$this->$k = $v;
}
}
// 加密
public function aesEn($data){
return base64_encode(openssl_encrypt($data, $this->method,$this->key, OPENSSL_RAW_DATA , $this->iv));
}
//解密
public function aesDe($data){
return openssl_decrypt(base64_decode($data), $this->method, $this->key, OPENSSL_RAW_DATA, $this->iv);
}
}
?>
使用一个专门的类来实现生成Sign和验证Sign
namespace app\common\lib;
use think\facade\Cache;
class IAuth{
/**
* md5 加密
*/
public static function serPassword($data){
return md5($data,\config('api.password_pre_halt'));
}
/**
* 生成每次请求Sign字符串
*/
public static function setSign($data = []) {
// 按字段排序
\ksort($data);
//拼接字符串数据
$string = \http_build_query($data);
//通过 aes 来加密
$string = (new Aes(\config('api.aes_key')))->aesEn($string);
return $string;
}
/**
* 检测sign是否正常
*/
public static function checkSignPass($data) {
$str = (new Aes(\config('api.aes_key')))->aesDe($data);
// 判断解析出来的数据是否为空
if(empty($str)){
return false;
}
// 字符串转数组
parse_str($str,$arr);
// 判断是否是数组,数组内的字段是否正确
if(!\is_array($arr) || empty($arr['mg'])){
return false;
}
// 检测缓存,如果有缓存,说明这个sign已经被使用
if(Cache::get($data)){
return false;
}
return true;
}
}
?>
备注:
'aes_key' =>[
'key' => 'reter4446fdfgdfgdfg', //加密key,这个可以随便定义
'iv' => md5(time(). uniqid(),true), //保证偏移量为16位
'method' => 'AES-128-CBC' //加密方式 # AES-256-CBC等,这个可以搭配的形式有很多,具体的你可以去了解一下AES加密算法
],
Sign验证的基础类:Common
sign验证基本上在每个网络请求中都需要做的,所有我们把Sign验证封装到基础类中,这个做会更合理。
namespace app\api\controller;
use think\Controller;
use app\common\lib\exception\ApiException;
use think\facade\Config;
use app\common\lib\IAuth;
use think\facade\Cache;
class Common extends Controller{
public $headers ='';
/**
* 初始化方法
*/
public function initialize(){
$this->checkRequestAuth();
}
/**
* 检测app请求的Sign数据是否合法
*/
public function checkRequestAuth(){
//1. 获取请求头
$headers = $this->request->header();
//2. 判断sign是否为空
if(empty($headers['sign'])){
throw new ApiException('sign不能为空',400);
}
//3.检验设备
if(!in_array($headers['app_types'],\config('api.apptypes'))){
throw new ApiException('设备型号不符合',400);
}
// 测试代码: 手动生成sign,实际开发中,这段代码是由客户端来执行.
$aesData = IAuth::setSign(\input('post.'));
//4.检验sign
if(!IAuth::checkSignPass($aesData)){
throw new ApiException('sign验证不通过!',401);
}
//5.设置缓存(未用到,只是用来学习缓存技术)
Cache::set($headers['sign'],1,\config('api.app_sign_cache_time'));
// 获取缓存
echo Cache::get($headers['sign']);
// 保存header中的值
$this->headers = $headers;
}
}
?>
在以前,我们服务端还会使用比对时间戳的方式来验证Sign是否过期,还是用本地缓存(Cache)技术来验证该Sign字符串是否已经被请求过,保证Signj机制更加安全。
更多推荐
php中aes加密菜鸟教程,ThinkPhp来实现AES加密和解密以及Sign的生成和验证
发布评论