thinkphp中api接口数据安全解决方案之sign检验
目录
封装校验类ApiAuth.php
namespace app\common\lib;
use app\common\lib\ApiAuth;
class ApiAuth
{
/**
* 1,sign算法加密
*/
public static function setSign($data = [])
{
//(1)按字段排序
ksort($data);
//(2)拼接数据
$sign_str = http_build_query($data);//作用是生成通过 URL 编码后的查询字符串。(即上面的 content=111&list=12&user=12 这个部分)
//(3)通过aes加密
return (new Aes())->encrypt($sign_str);
//$string = $this->encrypt($string);
//return $string;
}
/**
* 检查sign是否正常
* @param array $data
*/
public static function checkSign($data)
{
//解密
$str = (new Aes())->decrypt($data['sign']);
if (!$str) {
return false;
}
//将字符串你转成数组格式
parse_str($str, $arr);
//比较
if (!is_array($arr) || $arr['name'] != 'qipa250') {
return false;
}
return true;
}
}
}
Common类中调用校验方法
在api模块的控制器中,建立公共的common类,别的类继承此类
class Common extends Controller{//新建的公共文件 Common
public $headers = '';
/*
* 初始化方法
*/
public function _initialize()
{
$this->checkRequestAuth();
}
/*
* 检查每次app请求的数据是否合法
*/
public function checkRequestAuth()
{
//首先获取headers
//use think\Request;
$header = request()->header();
//halt($header);
//sign 加密需要 客户端工程师 解密 服务端工程师
//基础数据校验
//如果sign不存在,报错
if (empty($header['sign'])) {
throw new ApiException('签名不存在!');
}
if (empty($header['apptype'])) {
throw new ApiException('客户端不存在!');
}
//验证请求的app客户端是否合法
if (!in_array($header['apptype'], config('app.apptypes'))) {
throw new ApiException('客户端不合法!', 400);
}
//校验sign
$header_result = ApiAuth::checkSign($header);
dump($header_result);
die();
}
}
抛出异常类ApiException
<?php
namespace app\common\lib\exception;
//引用异常类
use think\Exception;
//继承异常类
class ApiException extends Exception
{
//自定义http状态码
public $message = '';
public $httpCode = 400;
public $code = 0;
/*
* 渲染抛出异常的状态码和信息
*/
public function __construct($message = "", $httpCode = 0, $code = 0)
{
$this->message = $message;
$this->httpCode = $httpCode?:400;
$this->code = $code;
}
}
app.php配置文件
<?php
return [
'admin_password_pre' => '_qipa250',//后台管理员密码加密后缀
'aeskey' => 'QiPa250',//aes 密钥,服务端和客户端保持一致
'aesiv' => '12345678901234567890123456789012',//aes iv,服务端和客户端保持一致
'apptypes' => ['ios', 'android', 'wechat'],
];
Aes加密解密类
namespace app\common\lib;
use app\common\lib\Aes;
class Aes
{
private $key = null;
private $iv = null;
public function __construct()
{
// 需要小伙伴在配置文件app.php中定义aeskey
$this->iv = config('app.aesiv');
$this->key = config('app.aeskey');
$this->key = hash('sha256', $this->key, true);
}
public function encrypt($input)
{
$data = openssl_encrypt($input, 'AES-256-CBC', $this->key, OPENSSL_RAW_DATA, $this->hexToStr($this->iv));
$data = base64_encode($data);
return $data;
}
public function decrypt($input)
{
$decrypted = openssl_decrypt(base64_decode($input), 'AES-256-CBC', $this->key, OPENSSL_RAW_DATA, $this->hexToStr($this->iv));
return $decrypted;
}
function hexToStr($hex)
{
$string = '';
for ($i = 0; $i < strlen($hex) - 1; $i += 2) {
$string .= chr(hexdec($hex[$i] . $hex[$i + 1]));
}
return $string;
}
}
postman请求,返回true 表示签名验证成功
返回false,表示签名验证失败