瀏覽代碼

登录页-检测版本控制登录

Michael Wang 3 年之前
父節點
當前提交
89097ca888

+ 18 - 0
src/constants/constants.ts

@@ -14,3 +14,21 @@ export const DOMAIN = env.DEV
 
 /** 限流请求的服务器 */
 export const LIMIT_SERVER = "https://tcc.qmth.com.cn";
+
+/** 严格检测exe的指纹 */
+export const HOST_FILE_HASH_MAP = new Map([
+  [
+    "pc-test.ea100.com.cn",
+    "7501fb797d0bac668e13f392b42e1b9f6464442c2ffc03c2a3243416ef226eb8",
+  ],
+  [
+    "qmtest.exam-cloud.cn",
+    "a31c9eb84fe82031fb01e9e25262f0b407caa705b5245654faeed1490bff67f9",
+  ],
+  // [
+  //   "uestccourse.exam-cloud.cn",
+  //   "1ebdaa1e00fbefca0f6519e252e4d372bcb9c3f96c45760b032ca58683d28306",
+  // ],
+]);
+
+export const STRICT_CHECK_HOSTS = [...HOST_FILE_HASH_MAP.keys()];

+ 11 - 6
src/features/UserLogin/UserLogin.vue

@@ -14,6 +14,7 @@ import { onMounted, watch } from "vue";
 import { useRouter } from "vue-router";
 import GeeTest from "./GeeTest.vue";
 import GlobalNotice from "./GlobalNotice.vue";
+import { useAppVersion } from "./useAppVersion";
 import { getElectronConfig } from "./useElectronConfig";
 import { checkExamInProgress } from "./useExamInProgress";
 import { getGeeTestConfig } from "./useGeeTestConfig";
@@ -51,12 +52,17 @@ const productName = $computed(
 );
 const allowLoginType = $computed(() => QECSConfig.LOGIN_TYPE ?? []);
 
+const { newVersionAvailable, checkNewVersion } = useNewVersion();
+const { disableLoginBtnBecauseAppVersionChecker } =
+  useAppVersion(newVersionAvailable);
+
 let disableLoginBtn = $computed(
-  () => disableLoginBtnBecauseNotTimeout
-  // (isElectron() &&
-  //   (this.disableLoginBtnBecauseRemoteApp ||
-  //     this.disableLoginBtnBecauseVCam)) ||
-  // this.disableLoginBtnBecauseAppVersionChecker ||
+  () =>
+    disableLoginBtnBecauseNotTimeout ||
+    (!import.meta.env.DEV &&
+      //   (this.disableLoginBtnBecauseRemoteApp ||
+      //     this.disableLoginBtnBecauseVCam)) ||
+      disableLoginBtnBecauseAppVersionChecker.value)
   // this.disableLoginBtnBecauseRefreshServiceWorker ||
   // this.disableLoginBtnBecauseNotAllowedNative
 );
@@ -120,7 +126,6 @@ watch(
 //#endregion
 
 const router = useRouter();
-const { newVersionAvailable, checkNewVersion } = useNewVersion();
 
 let loginBtnLoading = $ref(false);
 let disableLoginBtnBecauseNotTimeout = $ref(false);

+ 79 - 0
src/features/UserLogin/useAppVersion.ts

@@ -0,0 +1,79 @@
+import { DOMAIN, STRICT_CHECK_HOSTS } from "@/constants/constants";
+import ua from "@/utils/ua";
+import { checkMainExe, existsSync, isElectron } from "@/utils/utils";
+import { onMounted, Ref, watch } from "vue";
+
+export function useAppVersion(newVersionAvailable: Ref<boolean>) {
+  function checkApp() {
+    if (isElectron() && !eval(`process.env.PORTABLE_EXECUTABLE_FILE`)) {
+      disableLoginBtnBecauseAppVersionChecker = true;
+      if (ua.getBrowser().version !== "1.9.3") {
+        $message.error("请与学校申请最新的客户端,进行考试!", {
+          duration: 2 * 24 * 60 * 60,
+        });
+      } else {
+        $message.error(
+          "电脑环境达不到考试要求,请清理与考试无关软件或更换电脑",
+          {
+            duration: 2 * 24 * 60 * 60,
+            closable: false,
+          }
+        );
+      }
+    }
+
+    if (
+      [
+        "xjtu.ecs.qmth.com.cn",
+        "ccnu.ecs.qmth.com.cn",
+        "snnu.ecs.qmth.com.cn",
+        "swjtu.ecs.qmth.com.cn",
+      ].includes(DOMAIN)
+    ) {
+      if (!isElectron() || !existsSync("multiCamera.exe")) {
+        disableLoginBtnBecauseAppVersionChecker = true;
+        $message.error("请与学校申请最新的客户端,进行考试!", {
+          duration: 2 * 24 * 60 * 60,
+          closable: false,
+        });
+      }
+    }
+
+    if (STRICT_CHECK_HOSTS.includes(window.location.hostname)) {
+      if (!checkMainExe()) {
+        disableLoginBtnBecauseAppVersionChecker = true;
+        if (ua.getBrowser().version !== "1.9.3") {
+          $message.error("请与学校申请最新的客户端,进行考试!", {
+            duration: 2 * 24 * 60 * 60,
+          });
+        } else {
+          $message.error(
+            "电脑环境达不到考试要求,请清理与考试无关软件或更换电脑",
+            {
+              duration: 2 * 24 * 60 * 60,
+              closable: false,
+            }
+          );
+        }
+      }
+    }
+  }
+
+  onMounted(() => checkApp());
+
+  watch(newVersionAvailable, () => {
+    if (newVersionAvailable) {
+      $message.error("程序有更新,请重新打开客户端!", {
+        duration: 2 * 24 * 60 * 60,
+      });
+    }
+  });
+
+  let disableLoginBtnBecauseAppVersionChecker = $ref(false);
+
+  return {
+    disableLoginBtnBecauseAppVersionChecker: $$(
+      disableLoginBtnBecauseAppVersionChecker
+    ),
+  };
+}

+ 5 - 1
src/features/UserLogin/useNewVersion.ts

@@ -1,7 +1,10 @@
 import { onMounted, onUnmounted } from "vue";
 
+/**
+ * newVersionAvailable: boolean 是否有新版本
+ * checkNewVersion 有版本更新返回true,函数内部处理;没版本更新则返回false
+ */
 export function useNewVersion() {
-  /** 有版本更新返回true,函数内部处理;没版本更新则返回false */
   async function checkNewVersion(): Promise<boolean> {
     const myHeaders = new Headers();
     myHeaders.append("Content-Type", "application/javascript");
@@ -40,6 +43,7 @@ export function useNewVersion() {
 
       return true;
     }
+
     return false;
   }
 

+ 87 - 0
src/utils/utils.ts

@@ -1,6 +1,93 @@
+import { HOST_FILE_HASH_MAP } from "@/constants/constants";
+
 export function setUUID() {
   if (!localStorage.getItem("uuidForEcs")) {
     const uuidForEcs = "" + Date.now() + Math.random();
     localStorage.setItem("uuidForEcs", uuidForEcs);
   }
 }
+
+export function isElectron() {
+  return typeof window.nodeRequire != "undefined";
+}
+
+export function existsSync(path: string) {
+  const fs: typeof import("fs") = window.nodeRequire("fs");
+  return fs.existsSync(path);
+}
+
+export function checkMainExe() {
+  try {
+    let iid: string = window.nodeRequire("process").pid;
+    const cp: typeof import("child_process") =
+      window.nodeRequire("child_process");
+    iid = cp
+      .execSync(
+        `cmd /c chcp 65001>nul && C:\\Windows\\System32\\wbem\\wmic process where ^(processid^=${iid}^) get parentprocessid /value`
+      )
+      .toString();
+    iid = iid.replace("ParentProcessId=", "").trim();
+    console.log(iid);
+    logger({
+      cnl: ["local", "console", "server"],
+      key: "checkMainExe",
+      dtl: `iid1: ${iid}`,
+    });
+    iid = cp
+      .execSync(
+        `cmd /c chcp 65001>nul && C:\\Windows\\System32\\wbem\\wmic process where ^(processid^=${iid}^) get parentprocessid /value`
+      )
+      .toString();
+    iid = iid.replace("ParentProcessId=", "").trim();
+    logger({
+      cnl: ["local", "console", "server"],
+      key: "checkMainExe",
+      dtl: `iid2: ${iid}`,
+    });
+
+    const executablePathBuffer = cp.execSync(
+      `cmd /c chcp 65001>nul && C:\\Windows\\System32\\wbem\\wmic process where ^(processid^=${iid}^) get executablepath /value`
+    );
+    console.log(executablePathBuffer);
+    const encoding = window.nodeRequire("encoding");
+    // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+    let executablePath: string = encoding
+      .convert(executablePathBuffer, "utf8", "gbk")
+      .toString();
+    logger({
+      cnl: ["local", "console", "server"],
+      key: "checkMainExe",
+      dtl: executablePath,
+    });
+    executablePath = executablePath
+      .replace("ExecutablePath=", "")
+      .trim()
+      .replace(/&amp;/g, "&");
+    if (executablePath === eval(`process.env.PORTABLE_EXECUTABLE_FILE`)) {
+      const crypto: typeof import("crypto") = window.nodeRequire("crypto");
+      const fs: typeof import("fs") = window.nodeRequire("fs");
+      const getHash = crypto
+        .createHmac("sha256", "abcdefg")
+        .update(fs.readFileSync(executablePath))
+        .digest("hex");
+      console.log("the hash: ", getHash);
+      logger({
+        cnl: ["local", "console", "server"],
+        key: "checkMainExe",
+        dtl: `the hash: ${getHash}`,
+      });
+      if (HOST_FILE_HASH_MAP.get(window.location.hostname) === getHash) {
+        return true;
+      }
+    }
+    // check filepath executablePath md5
+  } catch (error) {
+    console.log(error);
+    logger({
+      cnl: ["local", "console", "server"],
+      key: "checkMainExe",
+      ejn: JSON.stringify(error),
+    });
+  }
+  return false;
+}