Sfoglia il codice sorgente

[Feat 0000] 单点登录前端实现

houzekong 10 mesi fa
parent
commit
1cfcfd63e7

+ 1 - 0
src/enums/cacheEnum.ts

@@ -1,3 +1,4 @@
+export const PWD_KEY = 'PWD__';
 // token key
 export const TOKEN_KEY = 'TOKEN__';
 

+ 43 - 0
src/hooks/vent/useSSO.ts

@@ -0,0 +1,43 @@
+// 本来应该是后端做,出于工期考虑转到前端
+
+import QueryString from 'qs';
+import { useUserStore } from '/@/store/modules/user';
+import { useRoute } from 'vue-router';
+
+/** 单点登录功能的Hook,该Hook是为了部署在同一局域网内的多套系统之间能够无缝切换 */
+export function useSSO() {
+  const userStore = useUserStore();
+  const route = useRoute();
+
+  /** 启用单点登录功能来跳转新的页面 */
+  function open(url: string, target?: string) {
+    const qs = QueryString.stringify({
+      username: userStore.userInfo?.username,
+      // 毫无意义的伪装,但我就是要装一下
+      id: userStore.getPassword,
+    });
+    window.open(`${url}?${qs}`, target);
+  }
+
+  /** 用在跳转到的页面上,执行单点登录的逻辑 */
+  function doSSO() {
+    if (!route.query) return;
+    const { username, id } = route.query;
+    if (!username || !id) return;
+    const realPassword = userStore.decryptPassword(id as string);
+    console.log('debug aaa', realPassword, username);
+    const params = {
+      username: username as string,
+      password: realPassword,
+      checkKey: new Date().getTime(),
+    };
+    userStore.login(params);
+  }
+
+  return {
+    /** 启用单点登录功能来跳转新的页面 */
+    open,
+    /** 用在跳转到的页面上,执行单点登录的逻辑 */
+    doSSO,
+  };
+}

+ 31 - 6
src/store/modules/user.ts

@@ -4,7 +4,16 @@ import { defineStore } from 'pinia';
 import { store } from '/@/store';
 import { RoleEnum } from '/@/enums/roleEnum';
 import { PageEnum } from '/@/enums/pageEnum';
-import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY, LOGIN_INFO_KEY, DB_DICT_DATA_KEY, TENANT_ID, OAUTH2_THIRD_LOGIN_TENANT_ID } from '/@/enums/cacheEnum';
+import {
+  ROLES_KEY,
+  TOKEN_KEY,
+  USER_INFO_KEY,
+  LOGIN_INFO_KEY,
+  DB_DICT_DATA_KEY,
+  TENANT_ID,
+  OAUTH2_THIRD_LOGIN_TENANT_ID,
+  PWD_KEY,
+} from '/@/enums/cacheEnum';
 import { getAuthCache, setAuthCache, removeAuthCache } from '/@/utils/auth';
 import { GetUserInfoModel, LoginParams, ThirdLoginParams } from '/@/api/sys/model/userModel';
 import { doLogout, getUserInfo, loginApi, phoneLoginApi, thirdLogin } from '/@/api/sys/user';
@@ -20,6 +29,7 @@ import { JDragConfigEnum } from '/@/enums/jeecgEnum';
 import { useSso } from '/@/hooks/web/useSso';
 import { getActions } from '/@/qiankun/state';
 import { useGlobSetting } from '/@/hooks/setting';
+import AES from 'crypto-js/aes';
 
 interface UserState {
   userInfo: Nullable<UserInfo>;
@@ -85,8 +95,17 @@ export const useUserStore = defineStore({
     hasShareTenantId(): boolean {
       return this.shareTenantId != null && this.shareTenantId !== '';
     },
+    /** 获取用户加密过的密码 */
+    getPassword() {
+      return getAuthCache<string>(PWD_KEY);
+    },
   },
   actions: {
+    /** 设置用户密码并加密,理论上加密、解密的工作应仅在此模块进行 */
+    setPassword(password: string) {
+      // setAuthCache(PWD_KEY, AES.encrypt(password, PWD_KEY));
+      setAuthCache(PWD_KEY, btoa(password));
+    },
     setToken(info: string | undefined) {
       this.token = info ? info : ''; // for null or undefined value
       setAuthCache(TOKEN_KEY, info);
@@ -141,6 +160,8 @@ export const useUserStore = defineStore({
         // save token
         this.setToken(token);
         this.setTenant(userInfo.loginTenantId);
+        this.setPassword(params.password);
+        // 将用户的用户名与密码存于数据库之中,这是为了
         return this.afterLoginAction(goHome, data);
       } catch (error) {
         return Promise.reject(error);
@@ -362,15 +383,19 @@ export const useUserStore = defineStore({
           password: 'autoAdmin123',
           checkKey: new Date().getTime(),
         };
-        const data = await loginApi(loginParams);
-        const { token } = data;
-        this.setToken(token);
-        const userInfo = data['userInfo'];
-        this.setUserInfo(userInfo);
+        this.login(loginParams);
       } catch (error) {
         return Promise.reject(error);
       }
     },
+    /** 解密用户密码,理论上加密、解密的工作应仅在此模块进行 */
+    decryptPassword(password: string) {
+      // const test1 = AES.encrypt('123123', '321');
+      // const test2 = AES.decrypt(test1, '321');
+      // console.log('debug', AES.decrypt(password, PWD_KEY));
+      return atob(password);
+      // return AES.decrypt(password, PWD_KEY).toString();
+    },
   },
 });
 

+ 0 - 1
src/utils/cache/memory.ts

@@ -96,7 +96,6 @@ export class Memory<T = any, V = any> {
   }
 
   clear() {
-    console.log('------clear------进入clear方法');
     Object.keys(this.cache).forEach((key) => {
       const item = this.cache[key];
       item.timeoutId && clearTimeout(item.timeoutId);

+ 4 - 2
src/utils/cache/persistent.ts

@@ -17,12 +17,14 @@ import {
   TENANT_ID,
   LOGIN_INFO_KEY,
   OAUTH2_THIRD_LOGIN_TENANT_ID,
+  PWD_KEY,
 } from '/@/enums/cacheEnum';
 import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting';
 import { toRaw } from 'vue';
 import { pick, omit } from 'lodash-es';
 
 interface BasicStore {
+  [PWD_KEY]: string | undefined;
   [TOKEN_KEY]: string | number | null | undefined;
   [USER_INFO_KEY]: UserInfo;
   [ROLES_KEY]: string[];
@@ -32,7 +34,7 @@ interface BasicStore {
   [DB_DICT_DATA_KEY]: string;
   [TENANT_ID]: string;
   [LOGIN_INFO_KEY]: LoginInfo;
-  [OAUTH2_THIRD_LOGIN_TENANT_ID]: string
+  [OAUTH2_THIRD_LOGIN_TENANT_ID]: string;
 }
 
 type LocalStore = BasicStore;
@@ -60,7 +62,7 @@ export class Persistent {
   static getLocal<T>(key: LocalKeys) {
     //update-begin---author:scott ---date:2022-10-27  for:token过期退出重新登录,online菜单还是提示token过期----------
     const globalCache = ls.get(APP_LOCAL_CACHE_KEY);
-    if(globalCache){
+    if (globalCache) {
       localMemory.setCache(globalCache);
     }
     //update-end---author:scott ---date::2022-10-27  for:token过期退出重新登录,online菜单还是提示token过期----------

+ 11 - 2
src/views/sys/login/Login.vue

@@ -19,7 +19,7 @@
   <!-- </AdaptiveContainer> -->
 </template>
 <script lang="ts" setup>
-  import { computed, ref } from 'vue';
+  import { computed, onMounted } from 'vue';
   import { AppLogo } from '/@/components/Application';
   import LoginForm from './LoginForm.vue';
   import { useGlobSetting } from '/@/hooks/setting';
@@ -29,6 +29,7 @@
   import { useLoginState } from './useLogin';
 
   import AdaptiveContainer from '/@/components/Container/src/Adaptive.vue';
+  import { useSSO } from '/@/hooks/vent/useSSO';
 
   defineProps({
     sessionTimeout: {
@@ -43,6 +44,12 @@
   const title = computed(() => globSetting?.title ?? '');
   const { handleBackLogin } = useLoginState();
   handleBackLogin();
+
+  const { doSSO } = useSSO();
+
+  onMounted(() => {
+    doSSO();
+  });
 </script>
 <style lang="less" scoped>
   @prefix-cls: ~'@{namespace}-login';
@@ -136,7 +143,9 @@
         color: rgb(224, 224, 224);
         font-size: 30px;
         text-align: center;
-        text-shadow: 1px 1px 1px #fff, -1px -1px 1px #000;
+        text-shadow:
+          1px 1px 1px #fff,
+          -1px -1px 1px #000;
       }
     }