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的生成和验证