const {ccclass, property} = cc._decorator; import CryptoJS = require('./crypto-js.min.js'); //引用AES源码js const BASE_URL = "https://api.sparkus.cn"; //只负责网络接口 次类只负责和后端交互,不负责处理数据 数据处理在GameTool @ccclass export default class HttpUtil extends cc.Component { static async getShareInfo(shareUrl: string): Promise { console.log("设置分享链接:",shareUrl); const time = Math.floor((new Date().getTime()) / 1000) const url = HttpUtil.apiSign(`/api/share/cfg?gameId=${config.gameId}&time=${time}&url=${shareUrl}`,{}) return this.post(url,null,null); } //排行榜 static async rankData(type,callback,data): Promise { const time = Math.floor((new Date().getTime()) / 1000) const url = HttpUtil.apiSign(`/api/get/rank/data?gameId=${config.gameId}&dataType=${type}&time=${time}`, data) this.post(url,data,callback); } static async uploadUserLogData(data,callback): Promise { const url = '/log/collect/data'; this.post(url,data,callback); } //暂时用不到 static async getUserRecord(data,callback): Promise { const time = Math.floor((new Date().getTime()) / 1000) const url = HttpUtil.apiSign(`/api/get/user/data?gameId=${config.gameId}&time=${time}`, data) this.post(url,data,callback); } static async post(url, data, callback) { const response = await this.fetchData(url, data, 'POST'); callback && callback(response); } static async get(url, callback) { const response = await this.fetchData(url, null, 'GET'); callback && callback(response); } static async fetchData(url, data, method) { const fullUrl = `${BASE_URL}${url}`; const headers = { 'Content-Type': 'application/json' }; const options = { method, headers, body: data ? JSON.stringify(data) : null, }; try { const response = await fetch(fullUrl, options); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error('Fetch error:', error); return null; } } /** * * @param url {string} 接口地址 * @param params {object} 需要加密的参数对象 */ static apiSign(url: string, params = {}) { let convertUrl = url.trim() if (convertUrl.indexOf('?') === -1) { convertUrl += '?' } // 传入参数转换拼接字符串 let postStr = getQueryString(params) const signedStr = genSignStr(convertUrl, postStr) const encryptStr = `sign=${signedStr}` let encryptSignStr = fxCry.encryptByDES(encryptStr, config.secretKey) encryptSignStr = encodeURIComponent(encryptSignStr) return `${urlencode(convertUrl)}&_p=${encryptSignStr}` } } function responseHandler(response: { data: any }) { return response.data } // 响应拦截器 // Rq.interceptors.response.use(responseHandler) const config = { gameId: "100010", secretKey: "wozrGKsL", EK:"hui231%1" }; interface CrypotoType { encryptByDES: any decryptByDES: any hmacSha256: any } class Crypoto implements CrypotoType { // 加密的向明值,自己根据项目实际情况定,需要跟后端开发保持一致 private keyHex = this.getHetKey() private getHetKey() { return CryptoJS.enc.Utf8.parse(config.EK); } /** DES加密 */ encryptByDES(message: string, secret?: string) { if(!message) { return message } const key = secret? CryptoJS.enc.Utf8.parse(secret): this.keyHex const encrypted = CryptoJS.DES.encrypt(message, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }); return encrypted.toString() } /** DES解密 */ decryptByDES(message: string, secret?: string) { const key = secret? CryptoJS.enc.Utf8.parse(secret): this.keyHex const decrypted = CryptoJS.DES.decrypt({ ciphertext: CryptoJS.enc.Base64.parse(message) }, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }) return decrypted.toString(CryptoJS.enc.Utf8) } /** hmacSHA256加密 */ hmacSha256(message: string, secret?: string) { const keyHex = secret? CryptoJS.enc.Utf8.parse(secret): this.keyHex const hash = CryptoJS.HmacSHA256(message, keyHex); return hash.toString() } /** hmacSHA256验证 */ verifyHmacSha256(message: string, signature: string) { const hash = CryptoJS.HmacSHA256(message, this.keyHex); return hash.toString() === signature } /** CBC加密 */ encryptCBC(word: string) { if (!word) { return word; } const srcs = CryptoJS.enc.Utf8.parse(word); const encrypted = CryptoJS.AES.encrypt(srcs, this.keyHex, { iv: this.keyHex, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.ZeroPadding }); return encrypted.toString(); } /** CBC解密 */ decryptCBC(word: string) { if (!word) { return word; } const encryptedHexStr = CryptoJS.enc.Hex.parse(word); const srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr); const decrypt = CryptoJS.AES.decrypt(srcs, this.keyHex, { iv: this.keyHex, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.ZeroPadding }); const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8); return decryptedStr.toString(); } } const fxCry = new Crypoto(); function isEmpty(data) { return data === "" || data === null || data === undefined || data.length === 0 || JSON.stringify(data) == "{}" } function getQueryString(obj) { // 首先对对象的键进行排序并过滤空值 const sortedKeys = Object.keys(obj).sort(); const sortedObj = {}; for (let i = 0; i < sortedKeys.length; i++) { if (isEmpty(obj[sortedKeys[i]])) { continue; } sortedObj[sortedKeys[i]] = obj[sortedKeys[i]]; } // 然后将排序后的对象转换为查询字符串 const params = []; for (const key in sortedObj) { params.push(`${encodeURIComponent(key)}=${encodeURIComponent(sortedObj[key])}`); } return params.join('&'); } /** * 组装签名字符串 * @param string url: 请求地址 * @param string postStr: post参数的a=1&b=2 * @returns */ function genSignStr(url: string, postStr: string): string { let lessUrl = url.replace('?', '') lessUrl = lessUrl + "&" + postStr return encodeURIComponent(fxCry.hmacSha256(lessUrl)) } // 对参数进行统一urlencode function urlencode(url: string): string { const [baseUrl, queryString] = url.split("?", 2); const params = new URLSearchParams(queryString); return `${baseUrl}?${params.toString()}`; }