huenchao / questions

每天想到的问题,都放在issue中。
6 stars 2 forks source link

ts req #45

Open huenchao opened 2 months ago

huenchao commented 2 months ago

import { sendLog } from "~background/log";
import { ENV, ENV_TAG, STORAGE_KEY_WANGWANG_MTOP_TOKEN } from "~common/const";
import { getExtensionLocalStorage } from "~common/utils";

export const CODE_NOT_LOGIN = 401;

interface MtopRequestConfig {
  api: string;
  v: string;
  data: any;

  method?: 'GET' | 'POST';

  prefix?: string;
  subDomain?: string;
  mainDomain?: string;
  appKey?: number;
  jsv?: string;
  type?:string
  ecode?: number;
  isSec?: number | string;
  Referer?: string;
  Origin?:string;
  referrerPolicy?: string;
  noWapa?: boolean;
  ignoreLogin?: boolean;
}

interface MtopResponse<T = any> {
  api: string;
  c?: string;
  data: T;
  ret: string[]
  traceId: string;
  v: string;
}

const DEFAULT_MTOP_REQUEST_CONFIG: Partial<MtopRequestConfig> = {
  v: '1.0',
  prefix: 'acs',
  subDomain: 'm',
  mainDomain: 'taobao.com',
  appKey: 12574478,
  jsv: '2.7.3',
  method: 'GET',
}

const MIN_RULE_ID = 1;
const MAX_RULE_ID = 10000;
let ruleId = MIN_RULE_ID;

export async function mtopRequest<T> (config: MtopRequestConfig) {
  const mergedConfig: MtopRequestConfig = {
    ...DEFAULT_MTOP_REQUEST_CONFIG,
    ...config
  }

  if (ENV_TAG !== ENV.PRODUCTION && !mergedConfig.noWapa) {
    mergedConfig.subDomain = 'wapa';
  }

  const method = mergedConfig.method;

  const fetchOnce = async () => {
    let { [STORAGE_KEY_WANGWANG_MTOP_TOKEN]: token } = await getExtensionLocalStorage(STORAGE_KEY_WANGWANG_MTOP_TOKEN);

    const now = Date.now();
    const sign = getSign(token, now, mergedConfig.appKey, mergedConfig.data);

    const url = new URL(`https://${mergedConfig.prefix}.${mergedConfig.subDomain}.${mergedConfig.mainDomain}/h5/${mergedConfig.api}/${mergedConfig.v}/`);

    url.searchParams.append('api', mergedConfig.api);
    url.searchParams.append('v', mergedConfig.v);
    url.searchParams.append('dataType', 'json');
    url.searchParams.append('type', 'originaljson');
    url.searchParams.append('jsv', mergedConfig.jsv);
    url.searchParams.append('appKey', mergedConfig.appKey.toString());
    url.searchParams.append('t', now.toString());
    url.searchParams.append('sign', sign);
    if(typeof mergedConfig.isSec !== 'undefined'){
        url.searchParams.append('isSec', mergedConfig.isSec.toString());
    }
    if (typeof mergedConfig.ignoreLogin !== 'undefined') {
      url.searchParams.append('ignoreLogin', mergedConfig.ignoreLogin.toString());
    }

    // 要用 URLSearchParams ,用 FormData 的话校验会失败,提示非法请求
    let formData: URLSearchParams = null;

    if (method === 'POST') {
      formData = new URLSearchParams();
      formData.append('data', JSON.stringify(mergedConfig.data));
    } else {
      url.searchParams.append('data', JSON.stringify(mergedConfig.data));
    }

    let statusCode = -1;

    return fetch(url, {
      "referrerPolicy":  mergedConfig.referrerPolicy ||  "strict-origin-when-cross-origin",
      "body": formData,
      "method": method,
      "mode": "cors",
      "credentials": "include"
    }).then(response => {
      if (!response.ok) {
        statusCode = response.status;
        return response.text().then(errorText => Promise.reject(new Error(errorText)));
      }
      return response.json() as Promise<MtopResponse<T>>;
    }).catch(e => {
      console.error(mergedConfig.api, e);
      sendLog({
        type: 'error',
        target: 'mtop',
        extra: {
          statusCode,
          message: e.message,
          request: {
            ...mergedConfig,
            data: mergedConfig.method === 'POST' ? void 0 : mergedConfig.data,
          }
        }
      });
      return;
    })
  }

  let retryCount = 0;

  // 在每次mtop调用前,设置拦截规则,取时间戳最后八位作为 ruleId
  const tempDynamicRuleId = ruleId;
  ruleId++;
  if (ruleId > MAX_RULE_ID) {
    ruleId = 1;
  }

  await new Promise(resolve => {
    chrome.declarativeNetRequest.updateDynamicRules({
      removeRuleIds: [tempDynamicRuleId]
    }, resolve);
  });

  const Referer = mergedConfig.Referer ||  "https://www.xxxx.com/";
  const Origin = mergedConfig.Origin ||  "https://www.xxxx.com";

  await new Promise(resolve => {
    chrome.declarativeNetRequest.updateDynamicRules({
      addRules: [{
        id: tempDynamicRuleId,
        priority: 1,
        action: {
          type: 'modifyHeaders',
          requestHeaders: [
            { header: "Origin", operation: "set", value: Origin },
            { header: "Referer", operation: "set", value: Referer },
          ]
        },
        condition: {
          urlFilter: mergedConfig.api,
          resourceTypes: ["xmlhttprequest"]
        }
      }]
    } as any, resolve);
  })

  // token失效时重试
  try {
    while (retryCount < 4) {
      retryCount++;
      const res = await fetchOnce();

      if (!res) {
        return;
      }

      const { data, ret } = res;

      if (Object.keys(data).length || ret?.[0].includes('SUCCESS')) {
        return data;
      } else if (ret[0]) {
        // 非法请求 or 令牌为空
        if (ret[0].includes('FAIL_SYS_ILLEGAL_ACCESS') || ret[0].includes('FAIL_SYS_TOKEN_EMPTY')) {
          // 会返回一个 c 字段,提供新的  _m_h5_tk 和 _m_h5_tk_enc
          if (res.c) {
            const [_m_h5_tk, _m_h5_tk_enc] = res.c.split(';')
            const newToken = _m_h5_tk.split('_')[0];
            // 更新token到存储
            await chrome.storage.local.set({ [STORAGE_KEY_WANGWANG_MTOP_TOKEN]: newToken });
          }
        } else if (ret[0].includes('FAIL_SYS_SESSION_EXPIRED')) {
          // Session过期 表示未登录
          return CODE_NOT_LOGIN;
        } else {
          sendLog({
            type: 'error',
            target: 'mtop',
            extra: {
              statusCode: 200,
              message: ret[0],
              request: {
                ...mergedConfig,
                data: mergedConfig.method === 'POST' ? void 0 : mergedConfig.data,
              }
            }
          });
        }
      }
    }

    return;
  } finally {
    chrome.declarativeNetRequest.updateDynamicRules({
      removeRuleIds: [tempDynamicRuleId]
    });
  }
}

function getSign (token: string, timestamp: number, appKey: number, data: any) {
  return md5(token + '&' + timestamp + "&" + appKey + "&" + JSON.stringify(data));
}

export function md5(string: any) {
  function rotateLeft(lValue: any, iShiftBits: any) {
      return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
  }
  function addUnsigned(lX: any, lY: any) {
      var lX4, lY4, lX8, lY8, lResult;
      lX8 = (lX & 0x80000000);
      lY8 = (lY & 0x80000000);
      lX4 = (lX & 0x40000000);
      lY4 = (lY & 0x40000000);
      lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
      if (lX4 & lY4) {
          return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
      }
      if (lX4 | lY4) {
          if (lResult & 0x40000000) {
              return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
          } else {
              return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
          }
      } else {
          return (lResult ^ lX8 ^ lY8);
      }
  }

  function f(x: any, y: any, z: any) {
      return (x & y) | ((~x) & z);
  }

  function g(x: any, y: any, z: any) {
      return (x & z) | (y & (~z));
  }

  function h(x: any, y: any, z: any) {
      return (x ^ y ^ z);
  }

  function i(x: any, y: any, z: any) {
      return (y ^ (x | (~z)));
  }

  function FF(a: any, b: any, c: any, d: any, x: any, s: any, ac: any) {
      a = addUnsigned(a, addUnsigned(addUnsigned(f(b, c, d), x), ac));
      return addUnsigned(rotateLeft(a, s), b);
  }

  function GG(a: any, b: any, c: any, d: any, x: any, s: any, ac: any) {
      a = addUnsigned(a, addUnsigned(addUnsigned(g(b, c, d), x), ac));
      return addUnsigned(rotateLeft(a, s), b);
  }

  function HH(a: any, b: any, c: any, d: any, x: any, s: any, ac: any) {
      a = addUnsigned(a, addUnsigned(addUnsigned(h(b, c, d), x), ac));
      return addUnsigned(rotateLeft(a, s), b);
  }

  function II(a: any, b: any, c: any, d: any, x: any, s: any, ac: any) {
      a = addUnsigned(a, addUnsigned(addUnsigned(i(b, c, d), x), ac));
      return addUnsigned(rotateLeft(a, s), b);
  }

  function convertToWordArray(string: any) {
      var lWordCount;
      var lMessageLength = string.length;
      var lNumberOfWords_temp1 = lMessageLength + 8;
      var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
      var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
      var lWordArray = new Array(lNumberOfWords - 1);
      var lBytePosition = 0;
      var lByteCount = 0;
      while (lByteCount < lMessageLength) {
          lWordCount = (lByteCount - (lByteCount % 4)) / 4;
          lBytePosition = (lByteCount % 4) * 8;
          lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
          lByteCount++;
      }
      lWordCount = (lByteCount - (lByteCount % 4)) / 4;
      lBytePosition = (lByteCount % 4) * 8;
      lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
      lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
      lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
      return lWordArray;
  }

  function wordToHex(lValue: any) {
      var WordToHexValue = "", WordToHexValue_temp = "", lByte, lCount;
      for (lCount = 0; lCount <= 3; lCount++) {
          lByte = (lValue >>> (lCount * 8)) & 255;
          WordToHexValue_temp = "0" + lByte.toString(16);
          WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
      }
      return WordToHexValue;
  }

  function utf8Encode(string: any) {
      string = string.replace(/\r\n/g, "\n");
      var utftext = "";

      for (var n = 0; n < string.length; n++) {

          var c = string.charCodeAt(n);

          if (c < 128) {
              utftext += String.fromCharCode(c);
          }
          else if ((c > 127) && (c < 2048)) {
              utftext += String.fromCharCode((c >> 6) | 192);
              utftext += String.fromCharCode((c & 63) | 128);
          }
          else {
              utftext += String.fromCharCode((c >> 12) | 224);
              utftext += String.fromCharCode(((c >> 6) & 63) | 128);
              utftext += String.fromCharCode((c & 63) | 128);
          }

      }

      return utftext;
  }

  var x = [],
   k, AA, BB, CC, DD, a, b, c, d,
   S11 = 7, S12 = 12, S13 = 17, S14 = 22,
   S21 = 5, S22 = 9 , S23 = 14, S24 = 20,
   S31 = 4, S32 = 11, S33 = 16, S34 = 23,
   S41 = 6, S42 = 10, S43 = 15, S44 = 21;

  string = utf8Encode(string);

  x = convertToWordArray(string);

  a = 0x67452301;
  b = 0xEFCDAB89;
  c = 0x98BADCFE;
  d = 0x10325476;

  for (k = 0; k < x.length; k += 16) {
      AA = a;
      BB = b;
      CC = c;
      DD = d;
      a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
      d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
      c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
      b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
      a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
      d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
      c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
      b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
      a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
      d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
      c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
      b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
      a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
      d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
      c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
      b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
      a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
      d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
      c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
      b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
      a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
      d = GG(d, a, b, c, x[k + 10], S22, 0x2441453);
      c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
      b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
      a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
      d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
      c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
      b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
      a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
      d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
      c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
      b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
      a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
      d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
      c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
      b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
      a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
      d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
      c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
      b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
      a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
      d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
      c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
      b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
      a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
      d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
      c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
      b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
      a = II(a, b, c, d, x[k + 0], S41, 0xF4292244);
      d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
      c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
      b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
      a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
      d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
      c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
      b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
      a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
      d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
      c = II(c, d, a, b, x[k + 6], S43, 0xA3014314);
      b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
      a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
      d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
      c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
      b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
      a = addUnsigned(a, AA);
      b = addUnsigned(b, BB);
      c = addUnsigned(c, CC);
      d = addUnsigned(d, DD);
  }

  var temp = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);

  return temp.toLowerCase();
}