ThinkPHP8后端访问uniCloud云函数URL化密钥签名认证访问策略

最近做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
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
可爱小逗逼的头像 - 鹿快
评论 抢沙发

请登录后发表评论

    暂无评论内容