diff --git a/platform-web-ui/package.json b/platform-web-ui/package.json index f45f772..2215210 100644 --- a/platform-web-ui/package.json +++ b/platform-web-ui/package.json @@ -31,7 +31,6 @@ "echarts": "^5.4.3", "element-plus": "2.4.1", "js-base64": "^3.7.5", - "js-cookie": "3.0.5", "lodash-es": "4.17.21", "mitt": "3.0.1", "normalize.css": "8.0.1", diff --git a/platform-web-ui/pnpm-lock.yaml b/platform-web-ui/pnpm-lock.yaml index 540d1a9..afd74b2 100644 --- a/platform-web-ui/pnpm-lock.yaml +++ b/platform-web-ui/pnpm-lock.yaml @@ -29,9 +29,6 @@ dependencies: js-base64: specifier: ^3.7.5 version: 3.7.5 - js-cookie: - specifier: 3.0.5 - version: 3.0.5 lodash-es: specifier: 4.17.21 version: 4.17.21 @@ -3362,11 +3359,6 @@ packages: nopt: 6.0.0 dev: true - /js-cookie@3.0.5: - resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} - engines: {node: '>=14'} - dev: false - /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true diff --git a/platform-web-ui/src/components/ImageUpload/index.vue b/platform-web-ui/src/components/ImageUpload/index.vue index c1ba0f4..ebe98cc 100644 --- a/platform-web-ui/src/components/ImageUpload/index.vue +++ b/platform-web-ui/src/components/ImageUpload/index.vue @@ -22,8 +22,7 @@ import { Plus } from "@element-plus/icons-vue" import type { UploadProps } from "element-plus" import { deleteFile } from "@/api/upload" import { getEnvBaseURLPrefix } from "@/utils" -import { getToken } from "@/utils/cache/cookies" -import { getCurrentTenant } from "@/utils/cache/local-storage" +import { getToken, getCurrentTenant } from "@/utils/cache/local-storage" const props = defineProps({ src: { diff --git a/platform-web-ui/src/components/UserAvatar/index.vue b/platform-web-ui/src/components/UserAvatar/index.vue index 59a16ef..9982039 100644 --- a/platform-web-ui/src/components/UserAvatar/index.vue +++ b/platform-web-ui/src/components/UserAvatar/index.vue @@ -20,8 +20,7 @@ import { Plus } from "@element-plus/icons-vue" import type { UploadProps } from "element-plus" import { deleteFile } from "@/api/upload" import { getEnvBaseURLPrefix } from "@/utils" -import { getToken } from "@/utils/cache/cookies" -import { getCurrentTenant } from "@/utils/cache/local-storage" +import { getToken, getCurrentTenant } from "@/utils/cache/local-storage" const props = defineProps({ src: { diff --git a/platform-web-ui/src/constants/cache-key.ts b/platform-web-ui/src/constants/cache-key.ts index 9e52c7e..7e3340f 100644 --- a/platform-web-ui/src/constants/cache-key.ts +++ b/platform-web-ui/src/constants/cache-key.ts @@ -2,15 +2,16 @@ const SYSTEM_NAME = "molly" /** 缓存数据时用到的 Key */ class CacheKey { - static readonly TOKEN = `${SYSTEM_NAME}-token-key` static readonly CONFIG_LAYOUT = `${SYSTEM_NAME}-config-layout-key` static readonly SIDEBAR_STATUS = `${SYSTEM_NAME}-sidebar-status-key` static readonly ACTIVE_THEME_NAME = `${SYSTEM_NAME}-active-theme-name-key` static readonly VISITED_VIEWS = `${SYSTEM_NAME}-visited-views-key` static readonly CACHED_VIEWS = `${SYSTEM_NAME}-cached-views-key` - static readonly TENANT_ID = `${SYSTEM_NAME}-tenant-id-key` - static readonly CONTROL_SIZE = `${SYSTEM_NAME}-control-size` - static readonly USER_AND_PASSWORD = `${SYSTEM_NAME}-user-and-password` + static readonly CONTROL_SIZE = `${SYSTEM_NAME}-control-size-key` + + static readonly TOKEN = `${SYSTEM_NAME}-token` + static readonly TENANT_ID = `${SYSTEM_NAME}-tenant-id` + static readonly USER_AND_PASSWORD = `${SYSTEM_NAME}-uap` } export default CacheKey diff --git a/platform-web-ui/src/router/permission.ts b/platform-web-ui/src/router/permission.ts index f275269..3dd5973 100644 --- a/platform-web-ui/src/router/permission.ts +++ b/platform-web-ui/src/router/permission.ts @@ -6,7 +6,7 @@ import { useNoticeStoreHook } from "@/store/modules/notice" import { ElMessage } from "element-plus" import { useTitle } from "@/hooks/useTitle" -import { getToken } from "@/utils/cache/cookies" +import { getToken } from "@/utils/cache/local-storage" import { fixBlankPage } from "@/utils/fix-blank-page" import { setRouteChange } from "@/hooks/useRouteListener" import { isWhiteList, loginUrl } from "@/config/white-list" diff --git a/platform-web-ui/src/store/modules/notice.ts b/platform-web-ui/src/store/modules/notice.ts index dbbbbfd..d5e93af 100644 --- a/platform-web-ui/src/store/modules/notice.ts +++ b/platform-web-ui/src/store/modules/notice.ts @@ -4,7 +4,7 @@ import { computed, ref } from "vue" import { Client } from "@stomp/stompjs" // @ts-ignore import SockJS from "sockjs-client/dist/sockjs.min.js" -import { getToken } from "@/utils/cache/cookies" +import { getToken } from "@/utils/cache/local-storage" import { useTenantStoreHook } from "./tenant" import { getEnvBaseURL } from "@/utils" import { IPushMessage } from "@/types/base" diff --git a/platform-web-ui/src/store/modules/tenant.ts b/platform-web-ui/src/store/modules/tenant.ts index d5c6fd2..7313bad 100644 --- a/platform-web-ui/src/store/modules/tenant.ts +++ b/platform-web-ui/src/store/modules/tenant.ts @@ -32,8 +32,7 @@ export const useTenantStore = defineStore("tenant", () => { // 重置当前操作的租户 const resetCurrentTenant = (): void => { - currentTenant.value = defaultTenant - setTenant(defaultTenant) + setCurrentTenant(defaultTenant) } // 获取当前租户 diff --git a/platform-web-ui/src/store/modules/user.ts b/platform-web-ui/src/store/modules/user.ts index 4512d64..830bac6 100644 --- a/platform-web-ui/src/store/modules/user.ts +++ b/platform-web-ui/src/store/modules/user.ts @@ -5,7 +5,7 @@ import { ElMessage } from "element-plus" import { useNoticeStoreHook } from "./notice" import { useSettingsStore } from "./settings" import { useTagsViewStore } from "./tags-view" -import { getToken, removeToken, setToken } from "@/utils/cache/cookies" +import { getToken, removeToken, setToken } from "@/utils/cache/local-storage" import { loginApi, getUserInfoApi, getUserPermsApi, logoutApi } from "@/api/login" import { type IPermsButton, ILoginData, IPermsMenus, ILoginUserInfo } from "@/types/pms" import { resetRouter } from "@/router" diff --git a/platform-web-ui/src/utils/cache/cookies.ts b/platform-web-ui/src/utils/cache/cookies.ts deleted file mode 100644 index 21d938f..0000000 --- a/platform-web-ui/src/utils/cache/cookies.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** 统一处理 Cookie */ - -import CacheKey from "@/constants/cache-key" -import Cookies from "js-cookie" - -export const getToken = () => { - return Cookies.get(CacheKey.TOKEN) -} -export const setToken = (token: string) => { - Cookies.set(CacheKey.TOKEN, token) -} -export const removeToken = () => { - Cookies.remove(CacheKey.TOKEN) -} diff --git a/platform-web-ui/src/utils/cache/local-storage.ts b/platform-web-ui/src/utils/cache/local-storage.ts index 37763b3..67761c1 100644 --- a/platform-web-ui/src/utils/cache/local-storage.ts +++ b/platform-web-ui/src/utils/cache/local-storage.ts @@ -6,9 +6,24 @@ import { type ThemeName } from "@/hooks/useTheme" import { type TagView } from "@/store/modules/tags-view" import { type LayoutSettings } from "@/config/layouts" import { type ISimpleTenant } from "@/types/base" -import { defaultTenant } from "@/utils" +import { defaultTenant, hashCode } from "@/utils" import { ILoginData } from "@/types/pms" import { encode, decode } from "js-base64" +import { isJSON } from "../validate" + +//#region token +export const getToken = () => { + return localStorage.getItem(CacheKey.TOKEN) +} + +export const setToken = (token: string) => { + localStorage.setItem(CacheKey.TOKEN, token) +} + +export const removeToken = () => { + localStorage.removeItem(CacheKey.TOKEN) +} +//#endregion //#region 系统布局配置 export const getConfigLayout = () => { @@ -74,7 +89,7 @@ export const setCachedViews = (views: string[]) => { //#region 租户选择 export const getCurrentTenant = () => { let jsonStr = localStorage.getItem(CacheKey.TENANT_ID) - if (jsonStr === null || jsonStr === undefined || jsonStr === "") { + if (!jsonStr || !isJSON(jsonStr)) { setCurrentTenant(defaultTenant) jsonStr = localStorage.getItem(CacheKey.TENANT_ID) } @@ -102,16 +117,47 @@ export const setControlSize = (size: string) => { //#region 获取保存的 用户名和密码 export const getUserAndPassword = () => { - const jsonStr = localStorage.getItem(CacheKey.USER_AND_PASSWORD) - if (jsonStr) { - return JSON.parse(decode(jsonStr)) as ILoginData + const base64Str = localStorage.getItem(CacheKey.USER_AND_PASSWORD) + if (base64Str) { + // 第一次 base64 解密 + const desVal1 = decode(base64Str) + if (desVal1) { + // 根据 userAgent 生成 hashCode + const hc = hashCode(navigator.userAgent) + // hashCode 。base64 加密 + const salt = encode(hc.toString()) + // 祛除盐 + const desVal2Str = desVal1.replaceAll(`${salt}=.`, "") + const desVal3Json = decode(desVal2Str) + if (isJSON(desVal3Json)) { + const data = JSON.parse(desVal3Json) as ILoginData + let saltPassword = decode(data.password) + saltPassword = saltPassword.replaceAll(`${hc}`, "") + const password = saltPassword.replaceAll(`${salt}`, "") + data.password = password + return data + } + } + removeUserAndPassword() } return null } // 保存 用户名和密码 export function setUserAndPassword(data: ILoginData) { - return localStorage.setItem(CacheKey.USER_AND_PASSWORD, encode(JSON.stringify(data))) + // 根据 userAgent 生成 hashCode + const hc = hashCode(navigator.userAgent) + // hashCode 。base64 加密 + const salt = encode(hc.toString()) + // 将 用户密码加盐,再加密 + data.password = encode(`${hc}${data.password}${salt}`) + // 将 用户名和密码转json 。base64 加密 + const encodeVal = encode(JSON.stringify(data)) + // 再 拼接上盐 + const newVal = `${salt}=.${encodeVal}` + // 再次 base64 加密 + const newVal2 = encode(newVal) + return localStorage.setItem(CacheKey.USER_AND_PASSWORD, newVal2) } // 删除 用户名和密码 diff --git a/platform-web-ui/src/utils/index.ts b/platform-web-ui/src/utils/index.ts index 12972ce..99f31e4 100644 --- a/platform-web-ui/src/utils/index.ts +++ b/platform-web-ui/src/utils/index.ts @@ -104,6 +104,24 @@ export const resetConfigLayout = () => { location.reload() } +/** + * 将字符串,转为 hashCode + * @param {string} str + * @returns {number} + */ +export const hashCode = (str: string) => { + let hash = 0 + if (str.length == 0) { + return hash + } + for (let i = 0; i < str.length; i++) { + const char = str.charCodeAt(i) + hash = (hash << 5) - hash + char + hash = hash & hash + } + return hash +} + /** * @param {string} url * @returns {Object} diff --git a/platform-web-ui/src/utils/service.ts b/platform-web-ui/src/utils/service.ts index e8d5077..c3acc69 100644 --- a/platform-web-ui/src/utils/service.ts +++ b/platform-web-ui/src/utils/service.ts @@ -2,7 +2,7 @@ import axios, { AxiosResponse, type AxiosInstance, type AxiosRequestConfig } fro import { useUserStoreHook } from "@/store/modules/user" import { ElMessage, ElMessageBox } from "element-plus" import { get, merge } from "lodash-es" -import { getToken } from "./cache/cookies" +import { getToken } from "./cache/local-storage" import { useTenantStoreHook } from "@/store/modules/tenant" import { useProjectStoreHook } from "@/store/modules/project" import { getEnvBaseURLPrefix } from "." @@ -159,7 +159,7 @@ function createRequest(service: AxiosInstance) { const projectId = useProjectStoreHook().getCurrentProjectId() // 如果是登录接口,就使用默认的 租户ID 进行登录 const tenantId = config.url === "/auth/login" ? defaultTenant.tenantId : useTenantStoreHook().getCurrentTenantId() - const defaultConfig = { + const defaultConfig: AxiosRequestConfig = { headers: { // 携带 Token Authorization: tokenValue ? tokenValue : "", @@ -167,6 +167,7 @@ function createRequest(service: AxiosInstance) { "x-project-id": projectId, "Content-Type": "application/json" }, + // 请求超时时间,10 秒 timeout: 10000, baseURL: getEnvBaseURLPrefix() } diff --git a/platform-web-ui/src/utils/validate.ts b/platform-web-ui/src/utils/validate.ts index 9b4564d..c8be2a9 100644 --- a/platform-web-ui/src/utils/validate.ts +++ b/platform-web-ui/src/utils/validate.ts @@ -118,3 +118,13 @@ export function isTelphone(value: string) { const reg = /^[1][0-9]{10}$|^0\d{2,3}-?\d{7,8}$/ return reg.test(value) } + +/** 字符串是否为 Json */ +export function isJSON(value: string) { + try { + JSON.parse(value) + return true + } catch (error) { + return false + } +} diff --git a/platform-web-ui/src/views/login/index.vue b/platform-web-ui/src/views/login/index.vue index 08ac208..9ee5d93 100644 --- a/platform-web-ui/src/views/login/index.vue +++ b/platform-web-ui/src/views/login/index.vue @@ -9,7 +9,7 @@ import { v4 as uuidv4 } from "uuid" import { getEnvBaseURL } from "@/utils" import { getUserAndPassword, setUserAndPassword, removeUserAndPassword } from "@/utils/cache/local-storage" import { type ILoginData } from "@/types/pms" -import { merge } from "lodash-es" +import { merge, clone } from "lodash-es" const router = useRouter() @@ -38,6 +38,7 @@ if (source) { source.codeText = "" merge(target, source) } + /** 登录表单数据 */ const loginFormData: ILoginData = reactive(target) @@ -66,12 +67,16 @@ const handleLogin = () => { .userLogin(loginFormData) .then(() => { // 登录成功后,是否记住密码 ? - loginFormData.rememberMe ? setUserAndPassword(loginFormData) : removeUserAndPassword() + if (loginFormData.rememberMe) { + const saveUserInfo = clone(loginFormData) + setUserAndPassword(saveUserInfo) + } else { + removeUserAndPassword() + } router.push({ path: "/" }) }) .catch(() => { createCode() - loginFormData.password = "" }) .finally(() => { loading.value = false diff --git a/platform-web-ui/src/views/sys/tenant/index.vue b/platform-web-ui/src/views/sys/tenant/index.vue index 9c045b4..16350b4 100644 --- a/platform-web-ui/src/views/sys/tenant/index.vue +++ b/platform-web-ui/src/views/sys/tenant/index.vue @@ -462,7 +462,6 @@ const handleAdd = () => { dialogVisible.value = true } - const handleCommand = (command: string, data: ISysTenant) => { switch (command) { case "ResetData": diff --git a/server/molly-pms/src/main/java/com/xaaef/molly/perms/service/impl/PmsUserServiceImpl.java b/server/molly-pms/src/main/java/com/xaaef/molly/perms/service/impl/PmsUserServiceImpl.java index a48f28f..9a2abb6 100644 --- a/server/molly-pms/src/main/java/com/xaaef/molly/perms/service/impl/PmsUserServiceImpl.java +++ b/server/molly-pms/src/main/java/com/xaaef/molly/perms/service/impl/PmsUserServiceImpl.java @@ -271,11 +271,12 @@ public class PmsUserServiceImpl extends BaseServiceImpl userMenus.putAll(menuMap); } } else { - // 获取当前用户,拥有的 角色ID - var roleIds = getLoginUser().getRoles().stream().map(PmsRoleDTO::getRoleId).collect(Collectors.toSet()); - if (roleIds.isEmpty()) { + var loginUser = getLoginUser(); + if (loginUser.getRoles() == null || loginUser.getRoles().isEmpty()) { return new UserRightsVO().setButtons(new HashSet<>()).setMenus(new ArrayList<>()); } + // 获取当前用户,拥有的 角色ID + var roleIds = loginUser.getRoles().stream().map(PmsRoleDTO::getRoleId).collect(Collectors.toSet()); // 根据 角色ID , 获取全部菜单 var menuIds = roleMapper.selectMenuIdByRoleIds(roleIds); if (menuIds.isEmpty()) { diff --git a/server/molly-service/src/main/resources/db/changelog/sql/pms_init.sql b/server/molly-service/src/main/resources/db/changelog/sql/pms_init.sql index 16caee9..1800ab9 100644 --- a/server/molly-service/src/main/resources/db/changelog/sql/pms_init.sql +++ b/server/molly-service/src/main/resources/db/changelog/sql/pms_init.sql @@ -54,6 +54,15 @@ VALUES -- Records of pms_user_role -- ---------------------------- INSERT INTO `pms_user_role` (`user_id`, `role_id`) VALUES +(19980817, 10001), +(6569516164032, 10002), +(6569516878992, 10003); + + +-- ---------------------------- +-- Records of pms_role_menu +-- ---------------------------- +INSERT INTO `pms_role_menu` (`role_id`, `menu_id`) VALUES (10001, 10001), (10001, 10002), (10001, 10003), @@ -131,6 +140,4 @@ INSERT INTO `pms_user_role` (`user_id`, `role_id`) VALUES (10003, 10007); - - SET FOREIGN_KEY_CHECKS = 1; diff --git a/server/plugins/auth-jwt/src/main/java/com/xaaef/molly/auth/service/impl/UserLoginServiceImpl.java b/server/plugins/auth-jwt/src/main/java/com/xaaef/molly/auth/service/impl/UserLoginServiceImpl.java index cb0279d..75b45b1 100644 --- a/server/plugins/auth-jwt/src/main/java/com/xaaef/molly/auth/service/impl/UserLoginServiceImpl.java +++ b/server/plugins/auth-jwt/src/main/java/com/xaaef/molly/auth/service/impl/UserLoginServiceImpl.java @@ -190,6 +190,7 @@ public class UserLoginServiceImpl implements UserLoginService { // 获取 登录用户,拥有的角色 var roles = roleService.listByUserId(target.getUserId()); if (roles.isEmpty()) { + target.setRoles(new HashSet<>()); return; } target.setRoles(roles); diff --git a/server/plugins/web-config/src/main/java/com/xaaef/molly/web/log/OperateLogAspect.java b/server/plugins/web-config/src/main/java/com/xaaef/molly/web/log/OperateLogAspect.java index a9efa02..363b077 100644 --- a/server/plugins/web-config/src/main/java/com/xaaef/molly/web/log/OperateLogAspect.java +++ b/server/plugins/web-config/src/main/java/com/xaaef/molly/web/log/OperateLogAspect.java @@ -82,7 +82,7 @@ public class OperateLogAspect { timeCost = System.currentTimeMillis() - startTime; } catch (Throwable ew) { ex = ew; - log.error(ew.getCause().getClass().getName(), ew.getMessage()); + log.error(ew.getMessage()); result = JsonResult.fail(ew.getMessage()); } finally { //方法执行完成后增加日志 diff --git a/tenant-web-ui/package.json b/tenant-web-ui/package.json index f45f772..2215210 100644 --- a/tenant-web-ui/package.json +++ b/tenant-web-ui/package.json @@ -31,7 +31,6 @@ "echarts": "^5.4.3", "element-plus": "2.4.1", "js-base64": "^3.7.5", - "js-cookie": "3.0.5", "lodash-es": "4.17.21", "mitt": "3.0.1", "normalize.css": "8.0.1", diff --git a/tenant-web-ui/pnpm-lock.yaml b/tenant-web-ui/pnpm-lock.yaml index 540d1a9..afd74b2 100644 --- a/tenant-web-ui/pnpm-lock.yaml +++ b/tenant-web-ui/pnpm-lock.yaml @@ -29,9 +29,6 @@ dependencies: js-base64: specifier: ^3.7.5 version: 3.7.5 - js-cookie: - specifier: 3.0.5 - version: 3.0.5 lodash-es: specifier: 4.17.21 version: 4.17.21 @@ -3362,11 +3359,6 @@ packages: nopt: 6.0.0 dev: true - /js-cookie@3.0.5: - resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} - engines: {node: '>=14'} - dev: false - /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true diff --git a/tenant-web-ui/src/components/ImageUpload/index.vue b/tenant-web-ui/src/components/ImageUpload/index.vue index c14eb41..d6d5a43 100644 --- a/tenant-web-ui/src/components/ImageUpload/index.vue +++ b/tenant-web-ui/src/components/ImageUpload/index.vue @@ -22,7 +22,7 @@ import { Plus } from "@element-plus/icons-vue" import type { UploadProps } from "element-plus" import { deleteFile } from "@/api/upload" import { getEnvBaseURLPrefix } from "@/utils" -import { getToken } from "@/utils/cache/cookies" +import { getToken } from "@/utils/cache/local-storage" import { useUserStoreHook } from "@/store/modules/user" const props = defineProps({ diff --git a/tenant-web-ui/src/components/UserAvatar/index.vue b/tenant-web-ui/src/components/UserAvatar/index.vue index 82bc67c..8b59c89 100644 --- a/tenant-web-ui/src/components/UserAvatar/index.vue +++ b/tenant-web-ui/src/components/UserAvatar/index.vue @@ -20,7 +20,7 @@ import { Plus } from "@element-plus/icons-vue" import type { UploadProps } from "element-plus" import { deleteFile } from "@/api/upload" import { getEnvBaseURLPrefix } from "@/utils" -import { getToken } from "@/utils/cache/cookies" +import { getToken } from "@/utils/cache/local-storage" import { useUserStoreHook } from "@/store/modules/user" const props = defineProps({ diff --git a/tenant-web-ui/src/constants/cache-key.ts b/tenant-web-ui/src/constants/cache-key.ts index 9e52c7e..b7bf30a 100644 --- a/tenant-web-ui/src/constants/cache-key.ts +++ b/tenant-web-ui/src/constants/cache-key.ts @@ -2,15 +2,17 @@ const SYSTEM_NAME = "molly" /** 缓存数据时用到的 Key */ class CacheKey { - static readonly TOKEN = `${SYSTEM_NAME}-token-key` static readonly CONFIG_LAYOUT = `${SYSTEM_NAME}-config-layout-key` static readonly SIDEBAR_STATUS = `${SYSTEM_NAME}-sidebar-status-key` static readonly ACTIVE_THEME_NAME = `${SYSTEM_NAME}-active-theme-name-key` static readonly VISITED_VIEWS = `${SYSTEM_NAME}-visited-views-key` static readonly CACHED_VIEWS = `${SYSTEM_NAME}-cached-views-key` - static readonly TENANT_ID = `${SYSTEM_NAME}-tenant-id-key` - static readonly CONTROL_SIZE = `${SYSTEM_NAME}-control-size` - static readonly USER_AND_PASSWORD = `${SYSTEM_NAME}-user-and-password` + static readonly CONTROL_SIZE = `${SYSTEM_NAME}-control-size-key` + + static readonly TOKEN = `${SYSTEM_NAME}-token` + static readonly USER_AND_PASSWORD = `${SYSTEM_NAME}-uap` + static readonly PROJECT_ID = `${SYSTEM_NAME}-project-id` + static readonly LAST_TENANT_ID = `${SYSTEM_NAME}-last-tenant-id` } export default CacheKey diff --git a/tenant-web-ui/src/layouts/components/Logo/index.vue b/tenant-web-ui/src/layouts/components/Logo/index.vue index 494c834..f31a220 100644 --- a/tenant-web-ui/src/layouts/components/Logo/index.vue +++ b/tenant-web-ui/src/layouts/components/Logo/index.vue @@ -1,4 +1,5 @@ @@ -23,11 +28,11 @@ const { layoutMode } = storeToRefs(settingsStore)
- + - + @@ -43,9 +48,11 @@ const { layoutMode } = storeToRefs(settingsStore) background-color: transparent; text-align: center; overflow: hidden; + .layout-logo { display: none; } + .layout-logo-text { height: 100%; vertical-align: middle; @@ -64,6 +71,7 @@ const { layoutMode } = storeToRefs(settingsStore) vertical-align: middle; display: inline-block; } + .layout-logo-text { display: none; } diff --git a/tenant-web-ui/src/router/permission.ts b/tenant-web-ui/src/router/permission.ts index 874493d..1b21f69 100644 --- a/tenant-web-ui/src/router/permission.ts +++ b/tenant-web-ui/src/router/permission.ts @@ -6,7 +6,7 @@ import { useNoticeStoreHook } from "@/store/modules/notice" import { ElMessage } from "element-plus" import { useTitle } from "@/hooks/useTitle" -import { getToken } from "@/utils/cache/cookies" +import { getToken } from "@/utils/cache/local-storage" import { fixBlankPage } from "@/utils/fix-blank-page" import { setRouteChange } from "@/hooks/useRouteListener" import { isWhiteList, loginUrl } from "@/config/white-list" @@ -89,10 +89,6 @@ async function initBasicData() { // 获取用户信息 if (userStore.userInfo === undefined || !userStore.userInfo) { await userStore.getUserInfo() - } - - // 获取当前登录用户,所在的租户信息 - if (tenantStore.tenant === undefined || !tenantStore.tenant) { await tenantStore.getLoginUserTenant() } diff --git a/tenant-web-ui/src/store/modules/notice.ts b/tenant-web-ui/src/store/modules/notice.ts index a90427b..bad8166 100644 --- a/tenant-web-ui/src/store/modules/notice.ts +++ b/tenant-web-ui/src/store/modules/notice.ts @@ -4,7 +4,7 @@ import { computed, ref } from "vue" import { Client } from "@stomp/stompjs" // @ts-ignore import SockJS from "sockjs-client/dist/sockjs.min.js" -import { getToken } from "@/utils/cache/cookies" +import { getToken } from "@/utils/cache/local-storage" import { useUserStoreHook } from "./user" import { getEnvBaseURL } from "@/utils" import { IPushMessage } from "@/types/base" diff --git a/tenant-web-ui/src/store/modules/project.ts b/tenant-web-ui/src/store/modules/project.ts index a377b0d..9bab8fe 100644 --- a/tenant-web-ui/src/store/modules/project.ts +++ b/tenant-web-ui/src/store/modules/project.ts @@ -3,10 +3,11 @@ import { defineStore } from "pinia" import { ref } from "vue" import { ISimpleProject } from "@/types/base" import { defaultProject } from "@/utils" +import { setCurrentProject as setProject, getCurrentProject as getProject } from "@/utils/cache/local-storage" export const useProjectStore = defineStore("project", () => { // 当前项目 - const currentProject = ref(defaultProject) + const currentProject = ref(getProject()) // 获取当前项目 const getCurrentProjectId = (): number => { @@ -24,13 +25,14 @@ export const useProjectStore = defineStore("project", () => { } // 设置当前项目信息 - const setCurrentProject = (tenant: ISimpleProject): void => { - currentProject.value = tenant + const setCurrentProject = (project: ISimpleProject): void => { + currentProject.value = project + setProject(project) } // 重置当前操作的项目 const resetCurrentProject = (): void => { - currentProject.value = defaultProject + setCurrentProject(defaultProject) } // 获取当前项目 diff --git a/tenant-web-ui/src/store/modules/user.ts b/tenant-web-ui/src/store/modules/user.ts index 9d73829..b8bc2a2 100644 --- a/tenant-web-ui/src/store/modules/user.ts +++ b/tenant-web-ui/src/store/modules/user.ts @@ -5,7 +5,7 @@ import { ElMessage } from "element-plus" import { useNoticeStoreHook } from "./notice" import { useSettingsStore } from "./settings" import { useTagsViewStore } from "./tags-view" -import { getToken, removeToken, setToken } from "@/utils/cache/cookies" +import { getToken, removeToken, setToken } from "@/utils/cache/local-storage" import { loginApi, getUserInfoApi, getUserPermsApi, logoutApi } from "@/api/login" import { type IPermsButton, ILoginData, IPermsMenus, ILoginUserInfo } from "@/types/pms" import { resetRouter } from "@/router" diff --git a/tenant-web-ui/src/utils/cache/cookies.ts b/tenant-web-ui/src/utils/cache/cookies.ts deleted file mode 100644 index 21d938f..0000000 --- a/tenant-web-ui/src/utils/cache/cookies.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** 统一处理 Cookie */ - -import CacheKey from "@/constants/cache-key" -import Cookies from "js-cookie" - -export const getToken = () => { - return Cookies.get(CacheKey.TOKEN) -} -export const setToken = (token: string) => { - Cookies.set(CacheKey.TOKEN, token) -} -export const removeToken = () => { - Cookies.remove(CacheKey.TOKEN) -} diff --git a/tenant-web-ui/src/utils/cache/local-storage.ts b/tenant-web-ui/src/utils/cache/local-storage.ts index 4cae22c..afb6266 100644 --- a/tenant-web-ui/src/utils/cache/local-storage.ts +++ b/tenant-web-ui/src/utils/cache/local-storage.ts @@ -7,6 +7,23 @@ import { type TagView } from "@/store/modules/tags-view" import { type LayoutSettings } from "@/config/layouts" import { ILoginData } from "@/types/pms" import { encode, decode } from "js-base64" +import { ISimpleProject } from "@/types/base" +import { defaultProject, hashCode } from ".." +import { isJSON } from "../validate" + +//#region token +export const getToken = () => { + return localStorage.getItem(CacheKey.TOKEN) +} + +export const setToken = (token: string) => { + localStorage.setItem(CacheKey.TOKEN, token) +} + +export const removeToken = () => { + localStorage.removeItem(CacheKey.TOKEN) +} +//#endregion //#region 系统布局配置 export const getConfigLayout = () => { @@ -44,6 +61,7 @@ export const setActiveThemeName = (themeName: ThemeName) => { //#endregion //#region 标签栏 + export const getVisitedViews = () => { const json = localStorage.getItem(CacheKey.VISITED_VIEWS) return JSON.parse(json ?? "[]") as TagView[] @@ -81,16 +99,47 @@ export const setControlSize = (size: string) => { //#region 获取保存的 用户名和密码 export const getUserAndPassword = () => { - const jsonStr = localStorage.getItem(CacheKey.USER_AND_PASSWORD) - if (jsonStr) { - return JSON.parse(decode(jsonStr)) as ILoginData + const base64Str = localStorage.getItem(CacheKey.USER_AND_PASSWORD) + if (base64Str) { + // 第一次 base64 解密 + const desVal1 = decode(base64Str) + if (desVal1) { + // 根据 userAgent 生成 hashCode + const hc = hashCode(navigator.userAgent) + // hashCode 。base64 加密 + const salt = encode(hc.toString()) + // 祛除盐 + const desVal2Str = desVal1.replaceAll(`${salt}=.`, "") + const desVal3Json = decode(desVal2Str) + if (isJSON(desVal3Json)) { + const data = JSON.parse(desVal3Json) as ILoginData + let saltPassword = decode(data.password) + saltPassword = saltPassword.replaceAll(`${hc}`, "") + const password = saltPassword.replaceAll(`${salt}`, "") + data.password = password + return data + } + } + removeUserAndPassword() } return null } // 保存 用户名和密码 export function setUserAndPassword(data: ILoginData) { - return localStorage.setItem(CacheKey.USER_AND_PASSWORD, encode(JSON.stringify(data))) + // 根据 userAgent 生成 hashCode + const hc = hashCode(navigator.userAgent) + // hashCode 。base64 加密 + const salt = encode(hc.toString()) + // 将 用户密码加盐,再加密 + data.password = encode(`${hc}${data.password}${salt}`) + // 将 用户名和密码转json 。base64 加密 + const encodeVal = encode(JSON.stringify(data)) + // 再 拼接上盐 + const newVal = `${salt}=.${encodeVal}` + // 再次 base64 加密 + const newVal2 = encode(newVal) + return localStorage.setItem(CacheKey.USER_AND_PASSWORD, newVal2) } // 删除 用户名和密码 @@ -99,3 +148,35 @@ export function removeUserAndPassword() { } //#endregion + +//#region 项目选择 + +// 获取当前操作的项目 +export const getCurrentProject = () => { + let jsonStr = localStorage.getItem(CacheKey.PROJECT_ID) + if (!jsonStr || !isJSON(jsonStr)) { + setCurrentProject(defaultProject) + jsonStr = localStorage.getItem(CacheKey.PROJECT_ID) + } + return JSON.parse(jsonStr!) as ISimpleProject +} + +// 设置当前操作的项目 +export const setCurrentProject = (project: ISimpleProject) => { + const jsonStr = JSON.stringify(project) + localStorage.setItem(CacheKey.PROJECT_ID, jsonStr) +} + +//#endregion + +// 获取 上一次登录的租户Id +export const getLastTenantId = () => { + return localStorage.getItem(CacheKey.LAST_TENANT_ID) +} + +// 设置 上一次登录的租户Id +export const setLastTenantId = (tenantId: string) => { + localStorage.setItem(CacheKey.LAST_TENANT_ID, tenantId) +} + +//#endregion diff --git a/tenant-web-ui/src/utils/index.ts b/tenant-web-ui/src/utils/index.ts index 925c2f6..b88ab0f 100644 --- a/tenant-web-ui/src/utils/index.ts +++ b/tenant-web-ui/src/utils/index.ts @@ -104,6 +104,24 @@ export const resetConfigLayout = () => { location.reload() } +/** + * 将字符串,转为 hashCode + * @param {string} str + * @returns {number} + */ +export const hashCode = (str: string) => { + let hash = 0 + if (str.length == 0) { + return hash + } + for (let i = 0; i < str.length; i++) { + const char = str.charCodeAt(i) + hash = (hash << 5) - hash + char + hash = hash & hash + } + return hash +} + /** * @param {string} url * @returns {Object} diff --git a/tenant-web-ui/src/utils/service.ts b/tenant-web-ui/src/utils/service.ts index 701ff24..3c426f4 100644 --- a/tenant-web-ui/src/utils/service.ts +++ b/tenant-web-ui/src/utils/service.ts @@ -2,7 +2,7 @@ import axios, { AxiosResponse, type AxiosInstance, type AxiosRequestConfig } fro import { useUserStoreHook } from "@/store/modules/user" import { ElMessage, ElMessageBox } from "element-plus" import { get, merge } from "lodash-es" -import { getToken } from "./cache/cookies" +import { getToken } from "./cache/local-storage" import { useProjectStoreHook } from "@/store/modules/project" import { getEnvBaseURLPrefix } from "." import { ISimpleProject } from "@/types/base" diff --git a/tenant-web-ui/src/utils/validate.ts b/tenant-web-ui/src/utils/validate.ts index 9b4564d..c8be2a9 100644 --- a/tenant-web-ui/src/utils/validate.ts +++ b/tenant-web-ui/src/utils/validate.ts @@ -118,3 +118,13 @@ export function isTelphone(value: string) { const reg = /^[1][0-9]{10}$|^0\d{2,3}-?\d{7,8}$/ return reg.test(value) } + +/** 字符串是否为 Json */ +export function isJSON(value: string) { + try { + JSON.parse(value) + return true + } catch (error) { + return false + } +} diff --git a/tenant-web-ui/src/views/login/index.vue b/tenant-web-ui/src/views/login/index.vue index 2ea8954..86c7e6c 100644 --- a/tenant-web-ui/src/views/login/index.vue +++ b/tenant-web-ui/src/views/login/index.vue @@ -7,9 +7,16 @@ import { User, Lock, Key, Picture, Loading } from "@element-plus/icons-vue" import ThemeSwitch from "@/components/ThemeSwitch/index.vue" import { v4 as uuidv4 } from "uuid" import { getEnvBaseURL } from "@/utils" -import { getUserAndPassword, setUserAndPassword, removeUserAndPassword } from "@/utils/cache/local-storage" +import { + getUserAndPassword, + setUserAndPassword, + removeUserAndPassword, + setLastTenantId, + getLastTenantId +} from "@/utils/cache/local-storage" import { type ILoginData } from "@/types/pms" -import { merge } from "lodash-es" +import { merge, clone } from "lodash-es" +import { useProjectStoreHook } from "@/store/modules/project" const router = useRouter() @@ -70,13 +77,23 @@ const handleLogin = () => { useUserStore() .userLogin(loginFormData) .then(() => { + // 获取 上一次登录的租户Id。如果和本次不同,清空项目缓存 + if (getLastTenantId() !== loginFormData.tenantId) { + // 将项目,重置为 默认项目 + useProjectStoreHook().resetCurrentProject() + } // 登录成功后,是否记住密码 ? - loginFormData.rememberMe ? setUserAndPassword(loginFormData) : removeUserAndPassword() + if (loginFormData.rememberMe) { + const saveUserInfo = clone(loginFormData) + setUserAndPassword(saveUserInfo) + } else { + removeUserAndPassword() + } + setLastTenantId(loginFormData.tenantId) router.push({ path: "/" }) }) .catch(() => { createCode() - loginFormData.password = "" }) .finally(() => { loading.value = false