最近做thinkPHP8项目,想在该项目的后台同时管理uniCloud云数据库(包括查看删除等操作),查看了官网URL化访问没有查看到云函数URL化访问的安全问题具体方案,于是自己做了一套加密策略。目前共享出来抛砖引玉,有更加简洁的方案,更加安全的方案请探讨。
思路:在后端页面加载的时候访问带header头的信息访问uniCloud云对象,云对象比对云约定的密钥+时间戳+随机串进行md5加密的签名,如果签名正确则放行。
下面看看具体代码: 后台前端简单代码如下:
mounted() {
axios.post(base_url+ '/UniCloudURL/getData').then(res => {
console.log(JSON.parse(res.data));
})
}
后端控制器代码如下:
<?php
namespace appmanagecontroller;
class UniCloudURL extends Admin
{
public function getData()
{
$url = '你的云对象地址/getunifunction/getlist';
// 设置你的前后端约定的密钥
$Token = config('my.token');
$randomStr = createNonceStr(16); //生成16位随机数,传个云对象
$timeStamp = time()*1000; // 当前时间戳,传给云对象
$signature = arithmetic($timeStamp,$randomStr, $Token); //此处签名的具体MD5算法,传给云对象
// 使用POST方式传递参数
$postData = json_encode([
'limit' => 5,
'offset' => 0
]);
$options = [
'http' => [
'method' => 'POST',
'header' => [
"Content-Type: application/json",
"userid:123",
"clienttimestamp:".$timeStamp,
"clientrandomstr:".$randomStr,
"clientsignature:".$signature,
],
'content' => $postData,
'timeout' => 10
],
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
]
];
$context = stream_context_create($options);
$response = @file_get_contents($url, false, $context);
if ($response === false) {
$error = error_get_last();
return json([
'success' => false,
'message' => '请求云对象失败',
'error' => $error['message'] ?? '未知错误',
'url' => $url
]);
}
// 解析响应检查token是否有效
$result = json_decode($response, true);
if (isset($result['code']) && $result['code'] === 403) {
return json([
'success' => false,
'message' => 'Token验证失败: ' . $result['message'],
'data' => null
]);
}
return $response;
}
}
下面位uniCloud云对象代码
'use strict';
const db = uniCloud.database();
const { arithmetic } = require('./utils.js') // 和PHP端一致的MD5签名方法
module.exports = {
// 这个方法就是预处理方法,实则就相当于拦截器
_before:async function() {
const clientInfo = this.getHttpInfo() // 获取PHP端访问传来的信息
const userid = clientInfo.headers.userid; // 这里这个userid是PHP传过来,用于在数据库查询得到密钥的,密钥和PHP端密钥一致,放在数据库为了提高安全
const key =await db.collection('url-token').where({_id:userid}).get();
const thisToken = key.data[0].accesskey // 数据库查询得到密钥
// 下面代码获取前端header携带的参数,注意这里的变量名全部小写,发现大写后不能正确获取到
let clientTimeStamp = clientInfo.headers.clienttimestamp;
let clientRandomStr = clientInfo.headers.clientrandomstr;
let clientSignature = clientInfo.headers.clientsignature;
// 1. Token验证
if (!clientSignature) {
throw new Error('缺少访问签名(令牌)')
}
let thisSignature = arithmetic(clientTimeStamp, clientRandomStr, thisToken);
if(clientSignature != thisSignature){
throw new Error('非法访问或者accesskey过期!')
}
},
async getlist() {
const httpInfo = JSON.parse(this.getHttpInfo().body)
try {
const {
limit = 10,
offset = 0,
} = httpInfo;
// 如果是普通用户,可以限制查询条件
let query = db.collection('uni-id-users').where({
dcloud_appid: '__UNI__2854232EA' // 这个地方查询的是同一个应用的
});
// 4. 执行查询
const result = await query
.field({
avatar_file: true,
mobile: true,
email: true,
username: true,
nickname: true,
token: true,
last_login_date: true
})
.limit(parseInt(limit))
.skip(parseInt(offset))
.get();
return {
code: 0,
message: '获取成功',
data: {
list: result.data,
total: result.affectedDocs,
limit: parseInt(limit),
offset: parseInt(offset)
}
};
} catch (error) {
console.error('获取数据失败:', error);
return {
code: 500,
message: '服务器错误: ' + error.message,
data: null
};
}
}
};
安全思考,没有把我的随机串方法和签名算法贴出来,需要探讨的朋友单独联系获取,后面出一期视频教程。(尊重劳动,转发注明出处blog.nxguozi.com)
© 版权声明
文章版权归作者所有,未经允许请勿转载。如内容涉嫌侵权,请在本页底部进入<联系我们>进行举报投诉!
THE END
暂无评论内容