UNPKG

t-comm

Version:

专业、稳定、纯粹的工具库

225 lines (218 loc) 10.2 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _classCallCheck = require('@babel/runtime/helpers/classCallCheck'); var _createClass = require('@babel/runtime/helpers/createClass'); var base64_decode = require('../../../../base64/decode.js'); require('../../../../base64/config.js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var _classCallCheck__default = /*#__PURE__*/_interopDefaultLegacy(_classCallCheck); var _createClass__default = /*#__PURE__*/_interopDefaultLegacy(_createClass); function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } /** 默认存储 key */ var DEFAULT_STORAGE_KEY = 'cocos_login_info'; /** * Cocos 端防刷拦截器 * * 加到请求拦截器的最后,会根据加密协议在请求 header 里加上防刷签名参数。 * 依赖业务提前请求 GetTs 接口获取加密参数(ts + encryptData)。 * * 加密协议见文档: * https://doc.weixin.qq.com/flowchart/f4_APsAyQbdAFwbbhTs281TFe1ncmazz?scode=AJEAIQdfAAor0oxY1IAPsAyQbdAFw * * 参考 pmd-npm/packages/business/src/network/interceptor/request/common/fangshua/fangshuaInterceptor.ts * * @example * ```ts * import { CocosFangshuaInterceptor } from 't-comm/cocos/network'; * * const fangshuaInterceptor = new CocosFangshuaInterceptor({ * storage, * getTs: () => appInfo.ts, * getEncryptData: () => appInfo.encryptData, * crypto: { * md5: (data) => CryptoJS.MD5(data).toString(), * aesDecrypt: (key, encryptedHex, iv) => { ... }, * }, * debug: true, // 开启调试日志(默认 false) * }); * ``` */ var CocosFangshuaInterceptor = /*#__PURE__*/function () { function CocosFangshuaInterceptor(options) { _classCallCheck__default["default"](this, CocosFangshuaInterceptor); this.options = options; } return _createClass__default["default"](CocosFangshuaInterceptor, [{ key: "interceptor", value: function interceptor(param) { var _a, _b; if (!needSign(param)) { return [false, param]; } var _this$options = this.options, storage = _this$options.storage, _this$options$storage = _this$options.storageKey, storageKey = _this$options$storage === void 0 ? DEFAULT_STORAGE_KEY : _this$options$storage, crypto = _this$options.crypto; var ts = this.options.getTs(); var loginInfo = (_a = storage === null || storage === void 0 ? void 0 : storage.get) === null || _a === void 0 ? void 0 : _a.call(storage, storageKey); var ouid = (loginInfo === null || loginInfo === void 0 ? void 0 : loginInfo.ouid) || ''; var accesstoken = (loginInfo === null || loginInfo === void 0 ? void 0 : loginInfo.accesstoken) || ''; var encryptData = this.options.getEncryptData(); var iv = 'ZdPwUq+f3OzuEWHP'; this.log('[fangshuaInterceptor]', { ts: ts, ouid: ouid, accesstoken: accesstoken, encryptData: encryptData }); if (!ts || !ouid || !accesstoken || !encryptData) { return [false, param]; } var body = param === null || param === void 0 ? void 0 : param.reqData; // 上报类请求需要检测是否为活跃上报 if (((_b = param === null || param === void 0 ? void 0 : param.url) === null || _b === void 0 ? void 0 : _b.indexOf('/tipmp.user.commreport.commreport/CommReport')) !== -1 && !isActiveReport(body)) { this.log('[fangshuaInterceptor] 不是活跃上报'); return [false, param]; } try { var decryptedText = crypto.aesDecrypt(crypto.md5(ouid + accesstoken), decodeEncryptData(encryptData), iv); var decryptedJson = JSON.parse(decryptedText); this.log('[fangshuaInterceptor] decryptedJson:', decryptedJson); var ukey = decryptedJson.ukey; var tipId = decryptedJson.req_id; var mock = isMock() ? '1' : '0'; var sig = crypto.md5(ts + ouid + crypto.md5(JSON.stringify(body)) + ukey + accesstoken + mock); // Cocos 小游戏环境统一使用 header(与小程序一致) param.header = param.header || {}; param.header.Tip_sig = sig; param.header.Tip_id = tipId; param.header.Tip_ts = ts; param.header.Tip_mock = mock; return [false, param]; } catch (error) { console.error('[CocosFangshuaInterceptor] 签名计算失败:', error); return [false, param]; } } /** 仅 debug=true 时打印日志 */ }, { key: "log", value: function log() { if (this.options.debug) { var _console; (_console = console).log.apply(_console, arguments); } } }]); }(); // 需要签名的 CGI 列表 var SIGN_CGI_LIST = [ // 防刷 '/tipmp.user.commreport.commreport/CommReport', '/pmdtrpc.commcgi.tipcgi.tipcgi/WaterProofReconfirm', // 高校 '/merc.plugin.usermgr_cgi.usermgr_cgi/VerifyAgeRange', '/merc.plugin.usermgr_cgi.usermgr_cgi/CheckStuAgeByIdCard', // 转化产品 '/merc.plugin.brandlott_cgi.brandlott_cgi/QueryBrandLottByMoreGameAttribute', '/merc.plugin.brandlott_cgi.brandlott_cgi/QueryFissionBrandLottActInfoByPlguin', '/merc.plugin.brandlott_cgi.brandlott_cgi/CheckInviteLoginGame', '/merc.plugin.brandlott_cgi.brandlott_cgi/V2CheckInviteLoginGame', '/merc.plugin.brandlott_cgi.brandlott_cgi/AcceptInviteLoginGame', '/merc.plugin.brandlott_cgi.brandlott_cgi/PointsWithdraw', '/merc.plugin.brandlott_cgi.brandlott_cgi/RecvExtraGift', '/merc.plugin.brandlott_cgi.brandlott_cgi/RecvLottTask', '/merc.plugin.brandlott_cgi.brandlott_cgi/LockTask']; /** * 判断请求是否需要带上防刷签名信息 * @param param 请求参数 * @returns 是否需要签名 */ function needSign(param) { var url = (param === null || param === void 0 ? void 0 : param.url) || ''; // eslint-disable-next-line no-useless-escape var pathRegex = /^(?:https?:\/\/)?[^\/?#]+(\/[^?#]*)/; var match = url.match(pathRegex); var urlPath = match ? match[1] || url : url; var isFound = SIGN_CGI_LIST.find(function (item) { return urlPath.includes(item); }); if (isFound) { return true; } // 检查接口是否要求开启:适合业务接口指定 if ((param === null || param === void 0 ? void 0 : param.enableTipSig) === true) { return true; } return false; } /** * 增加需要签名的 CGI 接口 * @param cgiList 需要签名的接口地址列表 */ function appendSignCGI() { var cgiList = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; cgiList === null || cgiList === void 0 ? void 0 : cgiList.forEach(function (url) { if (!SIGN_CGI_LIST.includes(url)) { SIGN_CGI_LIST.push(url); } }); } /** * 判断是否是活跃上报 * @param body post 请求的 body 内容 * @returns 本次上报内容是否是活跃上报 */ function isActiveReport(body) { var list = body === null || body === void 0 ? void 0 : body.list; if (list) { var _iterator = _createForOfIteratorHelper(list), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var event = _step.value; if (event.event_id === 'active') { return true; } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } return false; } /** * 对后台数据进行解密(Base64 → Hex) * 使用 t-comm 自带的 innerDecode(兼容小游戏环境,无需 window.atob) * @param encryptData 加密数据 * @returns 解密后的 hex 字符串 */ function decodeEncryptData(encryptData) { var decodedData = base64_decode.innerDecode(encryptData); var hexString = ''; for (var i = 0; i < decodedData.length; i++) { var hex = decodedData.charCodeAt(i).toString(16); hexString += hex.length === 1 ? "0".concat(hex) : hex; } hexString = hexString.toUpperCase(); return hexString; } /** * 当前运行环境是否是无头浏览器/自动化工具 * 在小游戏环境中通常返回 false,但保留检测逻辑以兼容开发者工具等场景 * @returns 是否是自动化环境 */ function isMock() { // 小游戏环境中 navigator/window 可能不存在,做安全检测 if (typeof navigator !== 'undefined') { var isHeadlessChrome = /HeadlessChrome/.test(navigator.userAgent || ''); if (isHeadlessChrome) return true; var isPuppeteer = 'puppeteer' in navigator; if (isPuppeteer) return true; if (navigator.webdriver) return true; } if (typeof window !== 'undefined') { var isPhantomJS = 'callPhantom' in window || '_phantom' in window; if (isPhantomJS) return true; } return false; } exports.CocosFangshuaInterceptor = CocosFangshuaInterceptor; exports.appendSignCGI = appendSignCGI; exports.isActiveReport = isActiveReport; exports.needSign = needSign;