首先输入用户名和密码,表单框进行校验,请求登录接口,获取token,在axios里面设置请求拦截器把token放入请求头中,使用token去请求用户信息,做菜单权限校验,拼接好路由,成功跳转到首页,如果有 RedirectUrl ,就跳转到 RedirectUrl。获取到的token和用户信息我们需要使用store持久化的能力缓存到LocalStorage/Cookie中去。
首先我们把需要保存的数据提前定义好,这里我们需要把token和userInfo信息报存在Cookie中,获取用户信息和退出登陆的接口我们也在这里统一声明了。
import { defineStore } from 'pinia'
import { getUserInfo, logout } from '@/api'
import type { UserState } from './model/userModel'
import type { UserInfo } from '@/api/user/types'
import { useAuthStore } from './auth'
import { RESEETSTORE } from '@/utils/reset'
export const useUserStore = defineStore({
id: 'app-user',
state: (): UserState => ({
token: '',
userInfo: null,
}),
actions: {
// setToken
setToken(token: string) {
this.token = token
},
// setUserInfo
setUserInfo(userInfo: UserInfo) {
this.userInfo = userInfo
},
async GetInfoAction() {
const { data } = await getUserInfo()
const { avatar, buttons, name, roles, routes } = data
const authStore = useAuthStore()
// 存储用户信息
this.setUserInfo({ avatar, name })
// 存储用户权限信息
authStore.setAuth({ buttons, roles, routes })
},
async Logout() {
await logout()
RESEETSTORE()
},
},
// 设置为true,缓存state
persist: true,
})
首先我们要定义一个登录用户名和密码以及验证码 input 框,然后在定义一个登录按钮和重置按钮,给登录按钮绑定一个click点击事件,点击登录之后向服务端提交用户和密码进行验证,验证成功服务器返回token。登录按钮点击事件业务代码如下:
const login = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async valid => {
if (!valid) return;
loading.value = true;
try {
// 1.执行登录接口
const { data } = await loginApi({ ...loginForm, password: md5(loginForm.password) });
userStore.setToken(data.access_token);
// 2.添加动态路由
await initDynamicRouter();
// 4.跳转到首页
router.push(HOME_URL);
ElNotification({
title: getTimeState(),
message: "欢迎登录",
type: "success",
duration: 3000
});
} finally {
loading.value = false;
}
});
};
用户登录成功之后,会在全局钩子router.beforeEach
中拦截路由,判断是否已获得token,在获得token之后我们就要去获取用户的基本信息了。我们在src/routes/initDynamicRouter.ts
文件中写下吗的逻辑:
/**
* @description 路由拦截 beforeEach
* */
router.beforeEach(async (to, from, next) => {
const userStore = useUserStore();
const authStore = useAuthStore();
// 1.NProgress 开始
NProgress.start();
// 3.判断是访问登陆页,有 Token 就在当前页面,没有 Token 重置路由到登陆页
if (to.path.toLocaleLowerCase() === LOGIN_URL) {
if (userStore.token) return next(from.fullPath);
resetRouter();
return next();
}
// 4.判断访问页面是否在路由白名单地址(静态路由)中,如果存在直接放行
if (ROUTER_WHITE_LIST.includes(to.path)) return next();
// 5.判断是否有 Token,没有重定向到 login 页面
if (!userStore.token) return next({ path: LOGIN_URL, replace: true });
// 6.如果没有菜单列表,就重新请求菜单列表并添加动态路由
if (!authStore.authMenuListGet.length) {
await initDynamicRouter();
return next({ ...to, replace: true });
}
// 7.存储 routerName 做按钮权限筛选
authStore.setRouteName(to.name as string);
// 8.正常访问页面
next();
});
/**
* @description 路由跳转错误
* */
router.onError(error => {
NProgress.done();
console.warn("路由错误", error.message);
});
/**
* @description 路由跳转结束
* */
router.afterEach(() => {
NProgress.done();
});
这里需要 authMenuListGet 做一个判断,因为 authMenuListGet 是存储在pinia中的,没有做持久化,当刷新页面的时候,它是空列表,就能执行后面的逻辑。做判断的话,每次刷新,都能从新请求userinfo接口。当用户的权限发生了改变,就能立马重置用户权限了。
权限控制的主体思路,前端会有一份路由表,它表示了每一个路由可访问的权限。当用户登录之后,通过 token 获取用户的 role 权限,动态根据用户的 role 算出其对应有权限的路由,再通过router.addRoute
动态挂载路由。对于不同权限的用户显示不同的侧边栏和限制其所能进入的页面。
1.创建vue实例将vue-router挂载,挂载路由的时候分为静态路由和动态路由,静态路由是公用页面,动态路由则是后端返回的路由数据
2.当用户登录后,获取 role,将 role 和路由表的数据进行权限比较,生成最终用户可访问的路由表
3.调用 router.addRoute(route)添加用户可访问的路由。
4.使用pinia管理路由表,根据pinia中可访问的路由渲染侧边栏 menu 组件
我们需要拿到后端返回的用户权限数据表,分别是 routes 表示用户拥有的路由权限 和 buttons 表示用户拥有的按钮权限。
在src/router下,有两份路由表,staticRouter.ts 和 dynamicRouter.ts,分别表示静态路由和动态路由。
静态路由定义如下:
import { RouteRecordRaw } from "vue-router";
import { HOME_URL, LOGIN_URL } from "@/config";
/**
* staticRouter (静态路由)
*/
export const staticRouter: RouteRecordRaw[] = [
{
path: "/",
redirect: HOME_URL
},
{
path: LOGIN_URL,
name: "login",
component: () => import("@/views/login/index.vue"),
meta: {
title: "登录"
}
},
{
path: "/layout",
name: "layout",
component: () => import("@/layouts/index.vue"),
// component: () => import("@/layouts/indexAsync.vue"),
redirect: HOME_URL,
children: []
}
];
/**
* errorRouter (错误页面路由)
*/
export const errorRouter = [
{
path: "/403",
name: "403",
component: () => import("@/components/ErrorMessage/403.vue"),
meta: {
title: "403页面"
}
},
{
path: "/404",
name: "404",
component: () => import("@/components/ErrorMessage/404.vue"),
meta: {
title: "404页面"
}
},
{
path: "/500",
name: "500",
component: () => import("@/components/ErrorMessage/500.vue"),
meta: {
title: "500页面"
}
},
// Resolve refresh page, route warnings
{
path: "/:pathMatch(.*)*",
component: () => import("@/components/ErrorMessage/404.vue")
}
];
静态路由需要在createRouter的时候就使用:
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import { staticRoutes } from './constantRoutes'
const router = createRouter({
history: createWebHashHistory(),
routes: staticRoutes as RouteRecordRaw[],
// 刷新时,滚动条位置还原
scrollBehavior: () => ({ left: 0, top: 0 }),
})
export default router
在dynamicRouter() 这个函数中,我们先从后端获取了用户信息和路由信息,然后对路由进行过滤:
import router from "@/routers/index";
import { LOGIN_URL } from "@/config";
import { RouteRecordRaw } from "vue-router";
import { ElNotification } from "element-plus";
import { useUserStore } from "@/stores/modules/user";
import { useAuthStore } from "@/stores/modules/auth";
/**
* @description 初始化动态路由
*/
export const initDynamicRouter = async () => {
const userStore = useUserStore();
const authStore = useAuthStore();
try {
// 1.获取菜单列表 && 按钮权限列表
await authStore.getAuthMenuList();
await authStore.getAuthButtonList();
// 2.判断当前用户有没有菜单权限
if (!authStore.authMenuListGet.length) {
ElNotification({
title: "无权限访问",
message: "当前账号无任何菜单权限,请联系系统管理员!",
type: "warning",
duration: 3000
});
userStore.setToken("");
router.replace(LOGIN_URL);
return Promise.reject("No permission");
}
// 3.添加动态路由
authStore.flatMenuListGet.forEach(item => {
item.children && delete item.children;
if (item.component && typeof item.component == "string") {
item.component = modules["/src/views" + item.component + ".vue"];
}
if (item.meta.isFull) {
router.addRoute(item as unknown as RouteRecordRaw);
} else {
router.addRoute("layout", item as unknown as RouteRecordRaw);
}
});
} catch (error) {
// 当按钮 || 菜单请求出错时,重定向到登陆页
userStore.setToken("");
router.replace(LOGIN_URL);
return Promise.reject(error);
}
};
这里我们拿到后端路由数据以后,使用 getShowMenuList 对路由做了匹配,匹配路由的时候我们根据路由的名称 meta.name 来匹配,最终拿到完整的routerList,然后通过循环和router.addRoute,把路由挨个添加到路由表中
function filterAsyncRoutes(
dynamicRoutes: RouteRecordRaw[],
authRouterList: string[],
) {
return dynamicRoutes.filter((route) => {
// 1.如果route的name在routeNames中没有, 直接过滤掉
if (!authRouterList.includes(route.name as string)) return false
// 2.如果当前route还有子路由(也就是有children), 需要对子路由也进行权限过滤
if (route.children && route.children.length > 0) {
route.children = filterAsyncRoutes(route.children, authRouterList)
}
return true
})
}
menu过滤:
function getMenuList(menuList: Menu.MenuOptions[]) {
const newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList))
return newMenuList.filter((item) => {
item.children?.length && (item.children = getMenuList(item.children))
return !item.meta?.isHide
})
}
在src/components下面新建Auth文件夹,这里写的组件直接全局注册的:
import { defineComponent, Fragment } from 'vue'
import { useAuthStore } from '@/store/modules/auth'
export default defineComponent({
name: 'Auth',
props: {
value: {
type: Array,
default: () => {
return []
},
},
},
setup(props, { slots }) {
const authStore = useAuthStore()
const currentPageRoles = authStore.authButtonList ?? []
const hasPermission = props.value.every(
(item) => item && currentPageRoles.includes(item as string),
)
return () => {
if (!slots) return null
return hasPermission ? <Fragment>{slots.default?.()}</Fragment> : null
}
},
})
使用方式:
<Auth :value="['btn.Role.update']">
<el-button
type="primary"
link
icon="Edit"
@click="openDialog('编辑', scope.row)"
>
编辑
</el-button>
</Auth>
到此为止,我们的路由权限和按钮权限逻辑已经处理完毕了。希望可以帮到大家。
文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99
文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效
文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是
文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件
文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件
文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码
文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware
文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停
文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待
文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析
文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code
文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象