CoderMrWu

生活诚可期,爱情价更高!

ThinkPhp来实现AES加密和解密以及Sign的生成和验证

Sign(签名) 是接口验证安全性的一种常用的技术,在一定程度上,可以保证我们数据接口得安全性, Sign一般都需要配合加密算法来使用,常用的AES系列算法(对称算法)、SHA系列算法(安全散列算法)、RSA系列算法(非对称算法)等,我这边现在使用的AES对称加密算法。

AES加密和解密示例代码

<?php
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

<?php

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验证封装到基础类中,这个做会更合理。

<?php
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机制更加安全。

 

点赞