Michael Wang 3 lat temu
rodzic
commit
dc24e83f04

+ 6 - 0
package.json

@@ -15,6 +15,9 @@
     "axios": "^0.21.4",
     "axios-progress-bar": "^1.2.0",
     "axios-retry": "^3.1.9",
+    "crypto-js": "^4.1.1",
+    "js-cookie": "^3.0.1",
+    "js-md5": "^0.7.3",
     "lodash-es": "^4.17.21",
     "mitt": "^3.0.0",
     "moment": "^2.29.1",
@@ -24,6 +27,9 @@
     "vue-router": "4.0.10"
   },
   "devDependencies": {
+    "@types/crypto-js": "^4.0.2",
+    "@types/js-cookie": "^2.2.7",
+    "@types/js-md5": "^0.4.3",
     "@types/lodash-es": "^4.17.4",
     "@types/node": "^16.7.11",
     "@types/ua-parser-js": "^0.7.36",

+ 3 - 20
src/api/loginPage.ts

@@ -1,24 +1,6 @@
 import { httpApp } from "@/plugins/axiosApp";
-// import {
-//   HistoryQueryParams,
-//   MarkHistoryOrderBy,
-//   MarkHistorySortField,
-//   Question,
-// } from "@/types";
-
-// /** 清理仲裁任务(libraryId 与其他参数互斥填写) */
-// export async function clearArbitrateTask(
-//   libraryId?: string,
-//   subjectCode?: string,
-//   groupNumber?: string
-// ) {
-//   const form = new FormData();
-//   libraryId && form.append("groupNumber", libraryId);
-//   subjectCode && form.append("subjectCode", subjectCode);
-//   groupNumber && form.append("groupNumber", groupNumber);
-//   return httpApp.post("/admin/exam/arbitrate/clear", form);
-// }
 
+/** 登录 */
 export function loginByUsername(loginInfo: {
   accountValue: string;
   password: string;
@@ -29,6 +11,7 @@ export function loginByUsername(loginInfo: {
   });
 }
 
+/** 登出 */
 export function logout() {
-  return httpApp.post("/api/ess/auth/logout");
+  // return httpApp.post("/api/ess/auth/logout");
 }

+ 33 - 0
src/auth/auth.ts

@@ -0,0 +1,33 @@
+import Cookies from "js-cookie";
+
+/**
+ * FIXME: 原本以为设置httponly的cookie可以防止token被XSS攻击,但是前端设置不了httponly的cookie。
+ * 理论上前端存储在cookie和localStorage的token都可能被盗。
+ */
+const TokenKey = "Token";
+
+export function getToken() {
+  return Cookies.get(TokenKey);
+}
+
+export function setToken(token: string) {
+  return Cookies.set(TokenKey, token);
+}
+
+export function removeToken() {
+  return Cookies.remove(TokenKey);
+}
+
+const SessionKey = "SessionKey";
+
+export function getSessionId() {
+  return Cookies.get(SessionKey);
+}
+
+export function setSessionId(sessionId: string) {
+  return Cookies.set(SessionKey, sessionId);
+}
+
+export function removeSessionId() {
+  return Cookies.remove(SessionKey);
+}

+ 8 - 0
src/constants/constants.ts

@@ -1 +1,9 @@
+import MD5 from "js-md5";
+
 export const YYYYMMDDHHmmss = "YYYY-MM-DD HH:mm:ss";
+export const PLATFORM = "WEB";
+
+if (!localStorage.getItem("deviceId")) {
+  localStorage.setItem("deviceId", MD5(Math.random() + "-" + Date.now()));
+}
+export const DEVICE_ID = localStorage.getItem("deviceId");

+ 1 - 0
src/features/Login/Login.vue

@@ -61,6 +61,7 @@ async function login() {
     console.log(res);
     store.setUserInfo(res.data);
   } catch (error) {
+    console.log(error);
     errorInfo.value = (<any>error).response.data.desc;
   }
 }

+ 32 - 2
src/plugins/axiosApp.ts

@@ -2,10 +2,12 @@ import Vue from "vue";
 import axios from "axios";
 // @ts-ignore
 import { loadProgressBar } from "axios-progress-bar";
-import cachingGet from "./axiosCache";
 import { notifyInvalidTokenThrottled } from "./axiosNotice";
+import { getToken, removeToken, getSessionId } from "../auth/auth";
 import axiosRetry from "axios-retry";
+import { PLATFORM, DEVICE_ID } from "@/constants/constants";
 import { message } from "ant-design-vue";
+import CryptoJS from "crypto-js";
 import { useMainStore } from "@/store";
 
 let store = null as unknown as ReturnType<typeof useMainStore>;
@@ -20,6 +22,19 @@ const cacheGetUrls: [RegExp] | [] = [];
 const _axiosApp = axios.create(config);
 axiosRetry(_axiosApp);
 
+function gToken(
+  method: "get" | "post",
+  url: string,
+  token: string,
+  time: number
+) {
+  const Authorization = `Token ${getSessionId()}:${CryptoJS.enc.Base64.stringify(
+    CryptoJS.SHA1("post&" + url + "&" + time + "&" + token)
+  )}`;
+
+  return Authorization;
+}
+
 _axiosApp.interceptors.request.use(
   function (config) {
     if (!store) {
@@ -28,6 +43,21 @@ _axiosApp.interceptors.request.use(
     if (config.setGlobalMask) {
       store.globalMask = true;
     }
+    const wk_token = getToken();
+    const time = Date.now();
+    if (wk_token) {
+      const completeURL = new URL("http://nasty.com" + config.url);
+      const path = completeURL.pathname;
+      config.headers.common["Authorization"] = gToken(
+        "post",
+        path,
+        wk_token,
+        time
+      );
+    }
+    config.headers.common["platform"] = PLATFORM;
+    config.headers.common["deviceId"] = DEVICE_ID;
+    config.headers.common["time"] = time;
     return config;
   },
   function (error) {
@@ -69,6 +99,7 @@ _axiosApp.interceptors.response.use(
     // 登录失效 跳转登录页面
     if (status == 403 || status == 401) {
       notifyInvalidTokenThrottled();
+      removeToken();
       return Promise.reject(error);
     } else if (status == 405) {
       if (showErrorMessage) {
@@ -101,7 +132,6 @@ _axiosApp.interceptors.response.use(
   }
 );
 
-// _axiosApp.get = cachingGet(_axiosApp, cacheGetUrls);
 loadProgressBar(null, _axiosApp);
 
 export const httpApp = _axiosApp;

+ 14 - 7
src/router/index.ts

@@ -1,19 +1,26 @@
-import { createRouter, createWebHistory } from "vue-router";
-import Login from "@/features/Login/Login.vue";
+import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
+import Login from "@/features/login/Login.vue";
+import Layout from "@/components/Layout.vue";
 
 const routes = [
   { path: "/", component: Login },
   { path: "/Login", component: Login, name: "Login" },
-  // {
-  //   path: "/admin/exam/inspected/start",
-  //   component: () => import("@/features/student/inspect/Inspect.vue"),
-  // },
+  {
+    path: "/admin/",
+    children: [
+      {
+        path: "rootOrg",
+        component: () => import("@/features/rootOrg/RootOrg.vue"),
+      },
+    ],
+    component: Layout,
+  },
   {
     path: "/:pathMatch(.*)*",
     name: "NotFound",
     component: () => import("@/components/404.vue"),
   },
-];
+] as RouteRecordRaw[];
 
 // 3. Create the router instance and pass the `routes` option
 // You can pass in additional options here, but let's

+ 4 - 0
src/store/index.ts

@@ -1,3 +1,4 @@
+import { setSessionId, setToken } from "@/auth/auth";
 import { Role } from "@/types";
 import { defineStore } from "pinia";
 
@@ -10,6 +11,7 @@ export const useMainStore = defineStore("main", {
       globalMask: false,
       userInfo: {
         userId: -1,
+        identity: "",
         displayName: "",
         rootOrgId: "",
         rootOrgName: "",
@@ -22,6 +24,8 @@ export const useMainStore = defineStore("main", {
   actions: {
     setUserInfo(res: any) {
       this.userInfo = res;
+      setToken(this.userInfo.token);
+      setSessionId(this.userInfo.identity);
     },
   },
 });

+ 30 - 0
yarn.lock

@@ -117,6 +117,21 @@
     core-js "^3.15.1"
     nanopop "^2.1.0"
 
+"@types/crypto-js@^4.0.2":
+  version "4.0.2"
+  resolved "https://registry.nlark.com/@types/crypto-js/download/@types/crypto-js-4.0.2.tgz#4524325a175bf819fec6e42560c389ce1fb92c97"
+  integrity sha1-RSQyWhdb+Bn+xuQlYMOJzh+5LJc=
+
+"@types/js-cookie@^2.2.7":
+  version "2.2.7"
+  resolved "https://registry.nlark.com/@types/js-cookie/download/@types/js-cookie-2.2.7.tgz#226a9e31680835a6188e887f3988e60c04d3f6a3"
+  integrity sha1-ImqeMWgINaYYjoh/OYjmDATT9qM=
+
+"@types/js-md5@^0.4.3":
+  version "0.4.3"
+  resolved "https://registry.nlark.com/@types/js-md5/download/@types/js-md5-0.4.3.tgz?cache=0&sync_timestamp=1629708102475&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fjs-md5%2Fdownload%2F%40types%2Fjs-md5-0.4.3.tgz#c134cbb71c75018876181f886a12a2f9962a312a"
+  integrity sha1-wTTLtxx1AYh2GB+IahKi+ZYqMSo=
+
 "@types/lodash-es@^4.17.4":
   version "4.17.5"
   resolved "https://registry.nlark.com/@types/lodash-es/download/@types/lodash-es-4.17.5.tgz?cache=0&sync_timestamp=1631454134838&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Flodash-es%2Fdownload%2F%40types%2Flodash-es-4.17.5.tgz#1c3fdd16849d84aea43890b1c60da379fb501353"
@@ -566,6 +581,11 @@ cross-spawn@^7.0.3:
     shebang-command "^2.0.0"
     which "^2.0.1"
 
+crypto-js@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.nlark.com/crypto-js/download/crypto-js-4.1.1.tgz?cache=0&sync_timestamp=1626954954639&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcrypto-js%2Fdownload%2Fcrypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf"
+  integrity sha1-nkhbzwNSEEG9hYRHhrg/t2GXNs8=
+
 csstype@^2.6.8:
   version "2.6.18"
   resolved "https://registry.nlark.com/csstype/download/csstype-2.6.18.tgz?cache=0&sync_timestamp=1631540658518&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcsstype%2Fdownload%2Fcsstype-2.6.18.tgz#980a8b53085f34af313410af064f2bd241784218"
@@ -931,6 +951,16 @@ joycon@^3.0.1:
   resolved "https://registry.npm.taobao.org/joycon/download/joycon-3.0.1.tgz#9074c9b08ccf37a6726ff74a18485f85efcaddaf"
   integrity sha1-kHTJsIzPN6Zyb/dKGEhfhe/K3a8=
 
+js-cookie@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.nlark.com/js-cookie/download/js-cookie-3.0.1.tgz#9e39b4c6c2f56563708d7d31f6f5f21873a92414"
+  integrity sha1-njm0xsL1ZWNwjX0x9vXyGHOpJBQ=
+
+js-md5@^0.7.3:
+  version "0.7.3"
+  resolved "https://registry.nlark.com/js-md5/download/js-md5-0.7.3.tgz#b4f2fbb0b327455f598d6727e38ec272cd09c3f2"
+  integrity sha1-tPL7sLMnRV9ZjWcn447Ccs0Jw/I=
+
 js-stringify@^1.0.2:
   version "1.0.2"
   resolved "https://registry.nlark.com/js-stringify/download/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db"