1.修复 登录用户未关联角色,报错

2.web 报错用户密码,盐加密
This commit is contained in:
Wang Chen Chen
2023-11-10 16:29:38 +08:00
parent bf224b8bf2
commit 5f5b0c4fbf
36 changed files with 280 additions and 106 deletions

View File

@@ -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",

View File

@@ -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

View File

@@ -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({

View File

@@ -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({

View File

@@ -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

View File

@@ -1,4 +1,5 @@
<script lang="ts" setup>
import { computed } from "vue"
import { storeToRefs } from "pinia"
import { useSettingsStore } from "@/store/modules/settings"
import logo from "@/assets/layouts/logo.png?url"
@@ -15,7 +16,11 @@ const props = withDefaults(defineProps<Props>(), {
})
const settingsStore = useSettingsStore()
const { tenant } = useTenantStoreHook()
const tenant = computed(() => {
return useTenantStoreHook().tenant
})
const { layoutMode } = storeToRefs(settingsStore)
</script>
@@ -23,11 +28,11 @@ const { layoutMode } = storeToRefs(settingsStore)
<div class="layout-logo-container" :class="{ collapse: props.collapse, 'layout-mode-top': layoutMode === 'top' }">
<transition name="layout-logo-fade">
<router-link v-if="props.collapse" key="collapse" to="/">
<img v-if="tenant" :src="tenant.logo" class="layout-logo" />
<img v-if="tenant" :key="tenant.tenantId" :src="tenant.logo" class="layout-logo" />
<img v-else :src="logo" class="layout-logo" />
</router-link>
<router-link v-else key="expand" to="/">
<img v-if="tenant" :src="tenant.logo" class="layout-logo-text" />
<img v-if="tenant" :key="tenant.tenantId" :src="tenant.logo" class="layout-logo-text" />
<img v-else :src="layoutMode !== 'left' ? logoText2 : logoText1" class="layout-logo-text" />
</router-link>
</transition>
@@ -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;
}

View File

@@ -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()
}

View File

@@ -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"

View File

@@ -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<ISimpleProject>(defaultProject)
const currentProject = ref<ISimpleProject>(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)
}
// 获取当前项目

View File

@@ -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"

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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}

View File

@@ -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"

View File

@@ -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
}
}

View File

@@ -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