PHP项目请求参数 加密和解密,thinkphp为例

为防止API请求的参数或者返回的结果内容暴露,所以对PHP项目请求的参数和响应的结果进行加密。

下面以ThinkPHP8 为例,也支持 thinkphp6。

准备:

ThinkPHP8 (API端)

vue3(前端,vue2适用)

要点:

一、服务端(API端)配置

①响应(参数)内容加密函数解析,代码说明:


$data = [];  //加密的数据 
$method = 'AES-128-CBC'; //加密方式  下面说明
$pwd='params'.time(); //加密密钥
$iv='params_iv_123456'; //初始化向量 长度根据加密方式来确定

//加密 使用openssl_encrypt函数进行AES加密

$result = openssl_encrypt(json_encode($data), "AES-128-CBC", $pwd, 0, $iv);
return $result;

/**
* 加密(解密)方式说明
*AES:高级加密标准。这是加密算法的名称(对称加密)。其他对称加密算法有:DES,3-DES等。
*128:这可能是指密钥大小。 AES加密使用3种密钥大小(128位,192位和256位)。 
*AES中的块大小也是128位。
*CBC:这是您想要的加密模式。有许多加密模式,这取决于您希望算法运行的速度,并行度和安全级别。
*一些模式*是CBC(密码块链接),ECB(电子密码本),CFB(密码反馈),CTR(计数器)等。
*
*例如:
*AES-128-ECB
*AES-256-CBC
*/

②请求(参数)内容解密,代码说明:


$data = '';  //解密字符串 
$method = 'AES-128-CBC'; //解密方式
$pwd='params'.time(); //解密密钥
$iv='params_iv_123456'; //初始化向量 长度根据解密方式来确定

//解密 使用openssl_decrypt函数进行AES解密

$result = openssl_decrypt($data, "AES-128-CBC", $pwd, 0, $iv);
return $result;

完整代码配置(ThinkPHP5以上的项目)在用于目录(app)下的 common.php 进行全局配置:

<?php
// 应用公共文件
use think\exception\HttpResponseException;
use think\Response;

/**
 * 应用公共文件
 * ①统一数据返回格式
 * ②参数加密
 * ③参数解密
 * ④图片/文件上传
 *
*/


/**
 *  统一返回数据格式
 *  状态码    描述
 * 200    请求成功
 * 204    请求成功,未返回实体,比如option请求,
 * 400    错误的请求
 * 401    认证失败,这个一般在token验证那里
 * 403    拒绝访问
 * 404    请求的资源不存在
 * 422    参数验证错误
 * 500    服务器错误
 */
function result(string $msg = 'error', array $data = [], int $code = 200, string $type = 'json'): Response
{
    $result = [
        "code" => $code,
        "msg" => $msg,
        "data" => $data
    ];
    // 调用Response的create方法,指定code可以改变请求的返回状态码
    return Response::create($result, $type)->code($code);
}

/**
 * 返回封装后的 API 数据到客户端
 * @access protected
 * @param mixed $msg 提示信息
 * @param mixed $data 要返回的数据
 * @param int $code 错误码,默认为0
 * @param string $type 输出类型,支持json/xml/jsonp
 * @param array $header 发送的 Header 信息
 * @return void
 * @throws HttpResponseException
 * 状态码    描述
 * 200    请求成功
 * 204    请求成功,未返回实体,比如option请求,
 * 400    错误的请求
 * 401    认证失败,这个一般在token验证那里
 * 403    拒绝访问
 * 404    请求的资源不存在
 * 422    参数验证错误
 * 500    服务器错误
 */
function suc($msg, $data = null, $code = 200)
{
    $time = time();
    $result = [
        'time' => $time,
        'key' => $msg,
    ];
    if (!empty($msg)) {
        $result['key'] = reqEncode($data, $time);;
    }
    return result($msg, $result, $code);
}

/**
 * 请求参数加密
 * openssl_decrypt
 * @param $encrypt_key  密钥
 * @param $iv 初始化向量 长度根据加密方式来确定
 */
function reqEncode($data, $ekey)
{
    $key = \config('req_key');
    $ekey = $ekey . $key['admin_key'];
    return openssl_encrypt(json_encode($data), "AES-128-CBC", $ekey, 0, $key['admin_iv']);
}

/**
 *  请求参数解密
 * openssl_decrypt
 * @param $encrypt_key  密钥
 * @param $iv 初始化向量
 */
function reqDecode()
{
    $params = request()->param();//请求获取的参数
    if (!empty($params)) {
        $key = \config('req_key');
        $ekey = $params['time'] . $key['admin_key'];
        $data = openssl_decrypt($params['key'], "AES-128-CBC", $ekey, 0, $key['admin_iv']);
        return json_decode($data, true);
    }
    return false;
}

/**
 * 后台处理 图片上传
 * 上传至本地服务器
 * @param $file 文件对象
 * @param $path 文件存储路径
 */
function uploadImage($path)
{
    // 获取上传文件
    $files = request()->file();
    //校验是否有非法图片上传
    $ckeck = check_illegal($files['file']);
    if(!$ckeck){
        return suc('非法图片','',1);
    }
    try {
        validate(['image'=>'fileSize:10240|fileExt:jpg,png,jpeg'])
            ->check($files); //验证类对上传文件的验证,包括文件大小、文件类型和后缀
        $savename = '';
        foreach($files as $file) {
            $savename= \think\facade\Filesystem::disk('public')->putFile( $path,$file,'md5'); //默认根据日期和微秒数生成文件命名规则。这里指定上传文件的命名规则 md5
            $savename = request()->domain().'/storage/'.$savename;
        }
        return suc('上传成功',$savename);
    } catch (\think\exception\ValidateException $e) {
        return suc($e->getMessage(),'',1);
    }
}

/**
 * 检测上传图片是否包含有非法代码
*/
function check_illegal($image)
{
    if (file_exists($image)) {
        $resource = fopen($image, 'rb');
        $fileSize = filesize($image);
        fseek($resource, 0);
        if ($fileSize > 512) { // 取头和尾
            $hexCode = bin2hex(fread($resource, 512));
            fseek($resource, $fileSize - 512);
            $hexCode .= bin2hex(fread($resource, 512));
        } else { // 取全部
            $hexCode = bin2hex(fread($resource, $fileSize));
        }
        fclose($resource);
        /* 匹配16进制中的 <% ( ) %> */
        /* 匹配16进制中的 <? ( ) ?> */
        /* 匹配16进制中的 <script | /script> 大小写亦可*/
        if (preg_match("/(3c25)|(3c3f.*?706870)|(3C534352495054)|(2F5343524950543E)|(3C736372697074)|(2F7363726970743E)/is", $hexCode)) {
            return false;
        }
    }
    return true;
}

二、前端配置(VUE配置)

因为服务端使用的 AES对称加密,所以前端也需要配置 AES 加密和解密。

① 安装扩展 : npm install crypto-js

② 在src目录下新增目录 utils 新增 crypto.js  配置加密解密函数:


// npm install crypto-js 
  
//请求参数加密类
/**
 * AES 对称加密(不安全)
 */
 import CryptoJS from "crypto-js";
 
export default {
  /**
   * 接口数据加密函数
   * @param str string 需加密的json字符串
   * @param key string 加密key
   * @param iv string 加密向量(16位)
   * @return string 加密密文字符串
   */
  encrypt(str, key, iv) {
    //密钥16位
       var key = CryptoJS.enc.Utf8.parse(key);
       //加密向量16位
       var iv = CryptoJS.enc.Utf8.parse(iv);
       var encrypted = CryptoJS.AES.encrypt(str, key, {
           iv: iv,
           mode: CryptoJS.mode.CBC,
           padding: CryptoJS.pad.Pkcs7
       });
       return encrypted.toString();
  },
 
  
   /**
   * 接口数据解密函数
   * @param str string 已加密密文
   * @param key string 加密key
   * @param iv string 加密向量(16位)
   * @returns {*|string} 解密之后的json字符串
   */
  decrypt(str, KEY, IV) {
    var key = CryptoJS.enc.Utf8.parse(KEY);
    var iv = CryptoJS.enc.Utf8.parse(IV);
    var decrypt = CryptoJS.AES.decrypt(str, key, {
      iv: iv,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    });
    return JSON.parse (decrypt.toString(CryptoJS.enc.Utf8));  //json 转为对象
  }
 
}
 

并且在 axios 请求中 进行加密和解密,这样达到全局使用配置。如下代码:

/**
 * get方法,对应get请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
const get = (url, params = {}, token = "") => {
  if (token != "") {
    axios.defaults.headers.common["X-CSRF-TOKEN"] = token;
  }
  let param = {};
  //参数不为空,加密
  if (Object.keys(params).length > 0) {
    const nowtime = parseInt(new Date().getTime() / 1000) + "";
    param.time = nowtime;
    params = JSON.stringify(params); //对象转为字符串
    param.key = crypto.encrypt(params, nowtime + reqKey, reqIv);
  }
  return new Promise((resolve, reject) => {
    axios
      .get(url, {
        params: param,
      })
      .then((res) => {
        let result = res.data;
        result.data = crypto.decrypt(
          result.data.key,
          result.data.time + reqKey,
          reqIv
        ); //解密
        resolve(result); //执行
      })
      .catch((err) => {
        // ElMessage.error(err.data.msg);
        reject(err.data);
      });
  });
};
/**
 * post方法,对应post请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
const post = (url, params = {}, token = "") => {
  if (token != "") {
    axios.defaults.headers.common["X-CSRF-TOKEN"] = token;
  }
  //参数不为空,加密
  let param = {};
  //参数不为空,加密
  if (Object.keys(params).length > 0) {
    const nowtime = parseInt(new Date().getTime() / 1000) + "";
    param.time = nowtime;
    params = JSON.stringify(params); //对象转为字符串
    param.key = crypto.encrypt(params, nowtime + reqKey, reqIv);
  }
  return new Promise((resolve, reject) => {
    axios
      .post(url, param)
      .then((res) => {
        let result = res.data;
        result.data = crypto.decrypt(
          result.data.key,
          result.data.time + reqKey,
          reqIv
        ); //解密
        resolve(result); //执行
      })
      .catch((err) => {
        // ElMessage.error(err.data.msg);
        reject(err.data);
      });
  });
};

以上服务端和客户端都配置完成了,下面可以进行使用了。

三、使用

数据接口加密:

(ThinkPHP5以上的项目)在用于目录(app)下的 common.php 进行封装API返回的数据

/**
*封装返回的数据,并加密,以当前时间为密钥组成数据
*/
function suc($msg, $data = null, $code = 200)
{
    $time = time();
    $result = [
        'time' => $time,
        'key' => $msg,
    ];
    if (!empty($msg)) {
        $result['key'] = reqEncode($data, $time);
    }
    return result($msg, $result, $code);
}

完整代码在上面common.php文件了。

输出结果示例:

客户端解密,已在http.js 请求 axios中配置了解密

结果:

至此,整个项目都可以对请求进行加密和解密的使用了。如果文中不明白的,也可以看下项目参考参考:

附上项目地址:

服务端(API)tp8blog: Thinkphp系统 博客API管理

客户端 VUE:https://gitee.com/IronTF/blogadmin_vue

并且开发了适用PHP项目的PHP扩展,更是易用简单。

composer require zishu/myextend

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值