php双向通信,RSA + AES 双向通信加密

导入插件

Vuex的modules模块里,初始化AES密匙,存做全局变量,例如我创建了 /store/modules/app.js 模块(对Vuex不甚明白的,看Vuex官方文档)

import { initAesKey, rsaEncrypt } from '@/js_sdk/encryption/utils';

const state = {

publicKey: null, // RSA加密公匙

aesKey: null, // AES加密密匙

aesEncryptKey: null, // AES加密密匙的RSA加密字符串

}

const mutations = {

SET_PUBLICKEY: (state, publicKey) => {

state.publicKey = publicKey

},

SET_AESKEY: (state, aesKey) => {

state.aesKey = aesKey

},

SET_AESENCRYPTKEY: (state, aesEncryptKey) => {

state.aesEncryptKey = aesEncryptKey

},

}

const actions = {

// 初始化请求,获取RSA公匙

async initPublicKey({

commit

}) {

// 这里的AppApi是我事先封装好的网络请求,用来向服务器获取公匙的,你要替换成你的网络请求

const res = await AppApi.getPublicKey()

if (res.code) {

// 存下RSA公匙

commit('SET_PUBLICKEY', res.data.key)

// 随机生成AES密匙,并存下来

commit('SET_AESKEY', initAesKey())

// 将AES密匙进行RSA加密,并存下来

commit('SET_AESENCRYPTKEY', rsaEncrypt(state.aesKey, state.publicKey))

}

return res;

}

}

export default {

namespaced: true,

state,

mutations,

actions

}

在main.js中调用app.js里的initPublicKey方法

// 初始化通信公匙

store.dispatch('app/initPublicKey').then((res) => {

// 获取公匙完成,开始加载网页

app.$mount()

})

完成2,3两步,现在你的APP在启动之时,即初始化了publicKey,aesKey,aesEncryptKey三个变量,接着我们来到http拦截器(如果你没有用到http拦截器,强烈建议你去插件市场搜索一款)

这里是我的http拦截器代码

import { aesEncrypt, aesDecrypt } from '@/js_sdk/encryption/utils';

/**

* 全局配置

* 只能配置 静态数据

* `content-type` 默认为 application/json

* `header` 中`content-type`设置特殊参数 或 配置其他会导致触发 跨域 问题,出现跨域会直接进入响应拦截器的catch函数中

*/

export const config = {

header: {

// 这里要注意,请求内容设置为 "text/plain",不要是其他的例如键值对类型

contentType: "text/plain"

}

}

/**

* 全局 请求拦截器, 支持添加多个拦截器

* 例如: 配置token、添加一些默认的参数

*

* `return config` 继续发送请求

* `return false` 会停止发送请求,不会进入错误数据拦截,也不会进入请求对象中的catch函数中

* `return Promise.reject('xxxxx')` 停止发送请求, 会错误数据拦截,也会进入catch函数中

*

* @param {Object} config 发送请求的配置数据

*/

globalInterceptor.request.use(

config => {

if(Vue.prototype.$store.getters.aesEncryptKey) {

// 将AES密匙(RSA加密后)放入请求头中

config['header']['aes-key'] = Vue.prototype.$store.getters.aesEncryptKey;

if(!config['data']) {

config['data'] = {};

}

// 将时间戳放入请求内容里面

config['data']['timetoken'] = Vue.prototype.$store.getters.timestamp;

// 将请求内容字符串化,进行AES加密

let data = aesEncrypt(JSON.stringify(config['data']), Vue.prototype.$store.getters.aesKey);

// 将请求内容替换成加密后的字符串,如果你的请求内容是键值对类型,这里会出错的

config['data'] = data;

}

return config;

},

err => {

console.error("is global fail request interceptor: ", err);

return false;

}

);

/**

* 全局 响应拦截器, 支持添加多个拦截器

* 例如: 根据状态码选择性拦截、过滤转换数据

*

* `return res` 继续返回数据

* `return false` 停止返回数据,不会进入错误数据拦截,也不会进入catch函数中

* `return Promise.reject('xxxxx')` 返回错误信息, 会错误数据拦截,也会进入catch函数中

*

* @param {Object} res 请求返回的数据

* @param {Object} config 发送请求的配置数据

* @return {Object|Boolean|Promise}

*/

globalInterceptor.response.use(

(res, config) => {

if (res.statusCode == 200) {

const code = parseInt(res.data.code);

if(code) {

// 如果有code,说明返回的是明文,这种请求也就是在初始化获取RSA公匙的那一次才会有

return res.data;

}else{

// AES解密

let content = aesDecrypt(res.data, Vue.prototype.$store.getters.aesKey);

// 明文json化,返回

return JSON.parse(content)

}

} else {

uni.showModal({

title: "请求错误",

showCancel: false,

content: "errMsg:" + res.errMsg + "\nstatusCode:" + res.statusCode

})

return Promise.reject(res, config);

}

},

(err, config) => {

// 请求错误, 有可能服务器没开, 有可能是跨域问题, 情况非常多, 不可能一一进行逻辑处理, 只能弹窗提示

uni.showModal({

title: "请求异常",

content: err.errMsg,

showCancel: false

})

return Promise.reject(err);

}

);

以上就是js端的代码,仅作参考,请根据自己代码进行调整

后端代码,这里暂时只提供php代码作为参考,以下是thinkphp6.0框架的模块中间件代码

namespace app\api\middleware;

use app\common\model\db\WebConfig;

use app\Request;

class Encryption

{

/**

* 处理请求

* @param $request

* @param \Closure $next

* @return mixed|\think\response\Json

*/

public function handle(Request $request, \Closure $next)

{

$aesKey = $request->getAesKey();

if ($aesKey && $aesKey != 'null') {

// 获取RSA私匙

$privateKey = WebConfig::getRSAPrivateKey();

// $encrypted为需要解密的数据(如果加密的时候用了base64,这里则需要解码),$decrypted为解密后的数据,$privateKey同上,为私钥密钥

openssl_private_decrypt(base64_decode($aesKey), $decrypted, $privateKey);

// 解密之后的字符串 $decrypted ,为aes密匙

$request->aesKey = $decrypted;

$content = $request->getContent();

// aes解密

$_content = aesDecrypt($content, $decrypted);

// 转json

$contentArr = json_decode(trim($_content), true);

if ($contentArr) {

foreach ($contentArr as $k => $v) {

$request->$k = $v;

}

}

if ($request->timetoken) {

// 这里的时间,请自己根据实际情况做调整,毕竟timetoken这是客户端时间,客户端时间很有可能不是准的,我是在客户端弄了socket连接通过心跳来校准时间

if (time() - $request->timetoken > 10 || time() - $request->timetoken < -5) {

// 时间异常

return json(config(TimeException));

}

} else {

return json(config(TimeException));

}

return $next($request);

} else {

// 没有AES密匙,要么非法,要么当前是 getPublicKey 方法

$pathinfo = $request->pathinfo();

$urls = explode('/', $pathinfo);

$controller = $urls[0];

$action = $urls[1];

if ($controller == 'web' && $action == 'getPublicKey') {

// 如果当前是在行获取公匙的路由,那么可以放行

return $next($request);

} else {

return json(config(AESNo));

}

}

}

}

php的RES加密解密方法如下

/**

* AES解密

* @param $content

* @param $key

* @return false|string

*/

function aesDecrypt($content, $key)

{

return openssl_decrypt($content, 'AES-256-CBC', $key, OPENSSL_ZERO_PADDING, 'ZZWBKJ_ZHIHUAWEI');

}

/**

* AES加密

* @param $content

* @param $key

* @return false|string

*/

function aesEncrypt($content, $key)

{

return openssl_encrypt($content, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, 'ZZWBKJ_ZHIHUAWEI');

}

thinkphp6的统一返回管理

/**

* 返回给前端数据标准化接口

* @param $data

* @param $msg

* @param $code

* @param $key

* @return false|string|\think\response\Json

*/

function apidata($data, $msg, $code, $key)

{

$content = [

"data" => $data,

"msg" => $msg,

"code" => $code,

];

if ($key) {

return base64_encode(aesEncrypt(json_encode($content), $key));

} else {

return json($content);

}

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值