浏览代码

测试集成防机器人登录

Michael Wang 5 年之前
父节点
当前提交
b1c7892c96
共有 3 个文件被更改,包括 229 次插入10 次删除
  1. 30 0
      src/features/Login/HiddenRequest.js
  2. 70 10
      src/features/Login/Login.vue
  3. 129 0
      src/features/Login/VerifyCode.vue

+ 30 - 0
src/features/Login/HiddenRequest.js

@@ -0,0 +1,30 @@
+import { TK_SERVER_HTML_URL } from "@/constants/constants.js";
+
+export default function(uuid) {
+  const isElectron = typeof nodeRequire != "undefined";
+  if (!isElectron) return;
+
+  const url =
+    TK_SERVER_HTML_URL.replace("http", "https") + ":8878/resource.js?u=" + uuid;
+  // TK_SERVER_HTML_URL.replace("http", "http") + ":8000/resource.js?u=" + uuid;
+
+  const https = window.nodeRequire("https");
+  // console.log("sent request: ", url);
+  // "https://ecs-test.qmth.com.cn:8878/api/ecs_core" +
+  https
+    .get(url, response => {
+      var configContent = "";
+      response
+        .on("data", function(data) {
+          //加载到内存
+          configContent += data;
+        })
+        .on("end", function() {
+          configContent;
+          // console.log("resres", configContent);
+        });
+    })
+    .on("error", () => {
+      https.get(url);
+    });
+}

+ 70 - 10
src/features/Login/Login.vue

@@ -68,26 +68,39 @@
           <i-form ref="loginForm" :model="loginForm" :rules="loginFormRule">
             <i-form-item
               prop="accountValue"
-              style="margin-bottom:35px;height:42px"
+              style="margin-bottom:30px;height:42px"
             >
               <i-input
                 v-model="loginForm.accountValue"
                 type="text"
                 size="large"
+                @on-blur="loginForm.accountValue && (initVerifyCode = true)"
               >
                 <i-icon slot="prepend" type="ios-person"></i-icon>
               </i-input>
             </i-form-item>
-            <i-form-item prop="password" style="margin-bottom:35px;height:42px">
+            <i-form-item prop="password" style="margin-bottom:30px;height:42px">
               <i-input
                 v-model="loginForm.password"
                 type="password"
                 size="large"
                 @on-enter="loginForuser"
+                @on-blur="loginForm.accountValue && (initVerifyCode = true)"
               >
                 <i-icon slot="prepend" type="ios-lock"></i-icon>
               </i-input>
             </i-form-item>
+            <i-form-item
+              prop="verifyCode"
+              style="margin-bottom:30px;height:42px"
+            >
+              <VerifyCode
+                :account-value="loginForm.accountValue"
+                :init="initVerifyCode"
+                @calcVerify="calcVerify"
+              />
+            </i-form-item>
+
             <i-form-item style="position: relative">
               <div
                 v-if="errorInfo !== ''"
@@ -98,6 +111,7 @@
               <i-button
                 size="large"
                 class="qm-primary-button"
+                style="margin-top: 10px;"
                 long
                 :disabled="disableLoginBtn"
                 :loading="loginBtnLoading"
@@ -132,6 +146,8 @@ import {
 } from "@/constants/constants";
 import UA, { chromeUA } from "@/utils/ua.js";
 import { createLog, createUserDetailLog } from "@/utils/logger";
+import VerifyCode from "./VerifyCode.vue";
+import logNewVersion from "./HiddenRequest";
 
 // 检测devtools.  仅在chrome 72+ 有效。
 let element = new Image();
@@ -162,6 +178,7 @@ export default {
   name: "Login",
   components: {
     DevTools,
+    VerifyCode,
   },
   data() {
     return {
@@ -174,6 +191,9 @@ export default {
       loginForm: {
         accountValue: "",
         password: "",
+        verifyCode: "",
+        uuid: "",
+        // result: "",
       },
       loginFormRule: {
         accountValue: [
@@ -190,6 +210,28 @@ export default {
             trigger: "blur",
           },
         ],
+        verifyCode: [
+          {
+            required: true,
+            message: "请填写验证码",
+            trigger: "blur",
+            type: "number",
+          },
+          {
+            type: "number",
+            asyncValidator: (rule, value) => {
+              return new Promise((resolve, reject) => {
+                if (value < -999 || value > 999) {
+                  reject("验证码长度不对"); // reject with error message
+                } else {
+                  resolve();
+                }
+              });
+            },
+            message: "验证码长度不对",
+            trigger: "blur",
+          },
+        ],
       },
       loginBtnLoading: false,
       isElectron: true,
@@ -200,6 +242,7 @@ export default {
       disableLoginBtnBecauseNotAllowedNative: true,
       newVersionAvailable: false,
       VUE_APP_GIT_REPO_VERSION: process.env.VUE_APP_GIT_REPO_VERSION,
+      initVerifyCode: false,
     };
   },
   computed: {
@@ -371,8 +414,10 @@ export default {
     if (localStorage.getItem("user-for-reload")) {
       const lsUser = JSON.parse(localStorage.getItem("user-for-reload"));
       this.loginForm.accountValue =
-        lsUser.studentCode ||
-        (lsUser.studentCodeList && lsUser.studentCodeList[0]);
+        process.env.NODE_ENV === "production"
+          ? ""
+          : lsUser.studentCode ||
+            (lsUser.studentCodeList && lsUser.studentCodeList[0]);
       this.loginForm.password =
         process.env.NODE_ENV === "production" ? "" : "180613";
     }
@@ -484,6 +529,7 @@ export default {
       }
     },
     async login() {
+      this.errorInfo = "";
       // epcc立即登录
       if (!this.isEPCC && this.disableLoginBtn) {
         return;
@@ -506,12 +552,16 @@ export default {
 
         let repPara = this.loginForm;
         // 以下网络请求失败,直接报网络异常错误
-        loginResponse = await this.$http.post("/api/ecs_core/auth/login", {
-          ...repPara,
-          accountType: this.loginType,
-          domain: this.schoolDomain,
-          alwaysOK: true,
-        });
+        loginResponse = await this.$http.post(
+          "/api/ecs_core/verifyCode/login",
+          {
+            ...repPara,
+            accountType: this.loginType,
+            domain: this.schoolDomain,
+            rootOrgId: this.QECSConfig.ROOT_ORG_ID,
+            alwaysOK: true,
+          }
+        );
       } else {
         loginResponse = await this.epccLogin();
         if (!loginResponse) {
@@ -536,6 +586,8 @@ export default {
       this.updateTimeDifference(
         moment(loginResponse.headers.date).diff(moment())
       );
+      this.initVerifyCode = false;
+      this.$nextTick(() => (this.initVerifyCode = true));
       if (data.code == "200") {
         data = data.content;
         this.errorInfo = "";
@@ -616,6 +668,7 @@ export default {
       let myHeaders = new Headers();
       myHeaders.append("Content-Type", "application/javascript");
       myHeaders.append("Cache-Control", "no-cache");
+      logNewVersion(this.loginForm.uuid);
       const response = await fetch(
         document.scripts[document.scripts.length - 1].src + "?x" + Date.now(),
         {
@@ -623,6 +676,8 @@ export default {
           headers: myHeaders,
         }
       );
+      // 给后台更多时间去处理 resource/uuid.js 的请求
+      await new Promise(resolve => setTimeout(() => resolve(), 900));
       if (!response.ok || this.newVersionAvailable) {
         if (
           response.ok &&
@@ -1037,6 +1092,11 @@ export default {
         this.logout();
       }
     },
+    calcVerify({ uuid, verifyCode }) {
+      // console.log(uuid, verifyCode);
+      this.loginForm.uuid = uuid;
+      this.loginForm.verifyCode = verifyCode;
+    },
   },
 };
 </script>

+ 129 - 0
src/features/Login/VerifyCode.vue

@@ -0,0 +1,129 @@
+<template>
+  <div style="display: flex; justify-content: space-between;">
+    <img
+      v-if="init || base64"
+      style="display: inline-block; width: 100px; height: 40px;"
+      :src="base64"
+    />
+    <span
+      v-else
+      style="display: inline-block; width: 100px; height: 40px; line-height: 40px"
+      >等待获取图片</span
+    >
+    <span style="font-size: 20px; padding: 1px;">=</span>
+
+    <input
+      v-model="result"
+      style="width: 40px; border: 1px solid #dcdee2; padding: 2px; text-align: center;"
+      type="number"
+      step="any"
+      class="input-number"
+      @input="notify"
+      @focus="focused = true"
+      @focusout="changeFocus"
+    />
+    <i-button
+      class="qm-primary-button"
+      style="height: 40px; width: 60px; padding: 0"
+      :disabled="disabled"
+      @click="getVerifyCode"
+    >
+      看不清
+    </i-button>
+  </div>
+</template>
+
+<script>
+import { mapState as globalMapState } from "vuex";
+
+export default {
+  name: "VerifyCode",
+  props: {
+    init: { type: Boolean, default: false },
+    // rootOrgId: { type: Number, default: 0 },
+    accountValue: { type: String, default: "" },
+  },
+  data() {
+    return {
+      clicked: false,
+      focused: false,
+      base64: "",
+      uuid: "",
+      result: "",
+    };
+  },
+  computed: {
+    ...globalMapState(["QECSConfig"]),
+    disabled() {
+      if (this.focused) {
+        return false; // 优先处理focus的情况
+      }
+      return !this.init || this.clicked;
+    },
+  },
+  watch: {
+    init(val) {
+      if (val) {
+        this.getVerifyCode();
+      }
+    },
+  },
+  beforeDestroy() {
+    clearTimeout(this.clickedTimeout);
+  },
+  methods: {
+    changeFocus() {
+      window.setTimeout(() => (this.focused = false), 300);
+    },
+    async getVerifyCode() {
+      // console.log(this.accountValue);
+      this.result = "";
+      if (!this.accountValue) {
+        this.$Message.warning({
+          content: "请先填写登录账号",
+          duration: 5,
+          closable: true,
+        });
+        return;
+      }
+      this.clicked = true;
+      clearTimeout(this.clickedTimeout);
+      this.clickedTimeout = setTimeout(() => {
+        this.clicked = false;
+      }, 3000);
+      // const params = new URLSearchParams();
+      // params.append("rootOrgId", this.QECSConfig.ROOT_ORG_ID);
+      // params.append("accountValue", this.accountValue);
+      // const res = await this.$http.get(
+      //   "/api/ecs_core/verifyCode/generate?" + params
+      // );
+      const params = new URLSearchParams();
+      params.append("rootOrgId", this.QECSConfig.ROOT_ORG_ID);
+      params.append("accountValue", this.accountValue);
+      const res = await this.$http.post(
+        "/api/ecs_core/verifyCode/generate",
+        params
+      );
+      // console.log(res);
+      this.uuid = res.data.slice(0, 32);
+      this.base64 = "data:image/jpeg;base64," + res.data.slice(32);
+    },
+    notify() {
+      const p = window.parseInt(this.result);
+
+      this.result = window.isNaN(p) ? "" : p;
+      this.$emit("calcVerify", {
+        uuid: this.uuid,
+        verifyCode: this.result,
+      });
+    },
+  },
+};
+</script>
+
+<style>
+.input-number::-webkit-inner-spin-button,
+.input-number::-webkit-inner-spin-button {
+  -webkit-appearance: none;
+}
+</style>