各位靓仔靓女,晚上好!我是你们今晚的权限管理特邀讲师。今天咱们就来聊聊 Vue 项目里的权限管理,让你的项目变得既安全又灵活,告别“一不小心就看到老板工资”的尴尬局面。
咱们的目标是打造一个可维护性杠杠的权限管理系统,包含路由权限、按钮权限和数据权限三大块。别害怕,我会用最接地气的方式,带你一步一步搞定它。
一、 权限管理之“用户登录与角色授权”
首先,权限管理的基石在于用户登录和角色授权。用户登录不用多说,就是验证用户的身份。角色授权则是给用户分配不同的角色,每个角色对应不同的权限。
-
用户数据模型:
先定义一个简单的用户数据模型,包含用户名、密码、角色等信息。
// 用户数据模型 const user = { username: 'admin', password: 'password', // 实际项目中密码要加密存储 roles: ['admin'] // 用户角色,可以有多个 };
-
角色与权限:
咱们来定义一下角色和权限的关系。可以用一个 JSON 对象来表示。
// 角色与权限的映射关系 const rolesPermissions = { 'admin': ['route.dashboard', 'button.add', 'data.all'], 'editor': ['route.article', 'button.edit', 'data.article'], 'visitor': ['route.home', 'data.public'] };
这里,
route.dashboard
表示仪表盘路由的权限,button.add
表示添加按钮的权限,data.all
表示所有数据的权限,以此类推。 -
登录流程:
登录流程大概是这样:
- 用户输入用户名和密码。
- 前端发送请求到后端进行验证。
- 后端验证成功后,返回用户信息,包括角色信息。
- 前端将用户信息存储到本地(例如 localStorage 或 sessionStorage)。
// 模拟登录 function login(username, password) { return new Promise((resolve, reject) => { setTimeout(() => { if (username === user.username && password === user.password) { resolve({ username: user.username, roles: user.roles }); } else { reject('用户名或密码错误'); } }, 500); // 模拟网络延迟 }); } // 在 Vue 组件中使用 methods: { handleLogin() { login(this.username, this.password) .then(res => { // 登录成功 localStorage.setItem('userInfo', JSON.stringify(res)); // 存储用户信息 this.$router.push('/dashboard'); // 跳转到仪表盘 }) .catch(err => { // 登录失败 alert(err); }); } }
二、 路由权限:让用户只能访问他们能访问的页面
路由权限是权限管理中最重要的一环。我们要确保用户只能访问他们有权限访问的页面。
-
Vue Router 导航守卫:
Vue Router 提供了导航守卫,可以在路由跳转前后进行一些操作。我们可以利用导航守卫来实现路由权限控制。
// router.js import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const routes = [ { path: '/home', name: 'home', component: () => import('../views/Home.vue'), meta: { requiresAuth: false } // 不需要登录即可访问 }, { path: '/dashboard', name: 'dashboard', component: () => import('../views/Dashboard.vue'), meta: { requiresAuth: true, permission: 'route.dashboard' } // 需要登录且需要 dashboard 路由权限 }, { path: '/article', name: 'article', component: () => import('../views/Article.vue'), meta: { requiresAuth: true, permission: 'route.article' } // 需要登录且需要 article 路由权限 }, { path: '/login', name: 'login', component: () => import('../views/Login.vue') }, { path: '*', redirect: '/home' } ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) router.beforeEach((to, from, next) => { const userInfo = localStorage.getItem('userInfo'); const parsedUserInfo = userInfo ? JSON.parse(userInfo) : null; const isLoggedIn = !!parsedUserInfo; if (to.meta.requiresAuth) { // 需要登录才能访问 if (isLoggedIn) { // 已登录,判断是否有权限 const requiredPermission = to.meta.permission; if (requiredPermission) { // 需要特定权限 const userRoles = parsedUserInfo.roles; let hasPermission = false; userRoles.forEach(role => { if (rolesPermissions[role] && rolesPermissions[role].includes(requiredPermission)) { hasPermission = true; } }); if (hasPermission) { next(); // 有权限,允许访问 } else { next('/home'); // 没有权限,跳转到首页 alert('您没有权限访问该页面'); } } else { next(); // 已登录,但不需要特定权限,允许访问 } } else { // 未登录,跳转到登录页面 next('/login'); } } else { // 不需要登录即可访问 next(); } }); export default router
这段代码做了以下几件事:
- 定义了
requiresAuth
和permission
两个 meta 属性,用于表示路由是否需要登录和需要的权限。 - 在
beforeEach
导航守卫中,判断用户是否已登录。 - 如果需要登录,判断用户是否有相应的权限。
- 如果没有权限,跳转到首页并提示。
- 如果未登录,跳转到登录页面。
- 定义了
-
动态路由:
如果路由非常多,而且权限管理非常复杂,可以考虑使用动态路由。动态路由是指根据用户的角色动态生成路由表。
// 假设后端返回的路由数据 const userRoutes = [ { path: '/dashboard', name: 'dashboard', component: () => import('../views/Dashboard.vue'), meta: { requiresAuth: true, permission: 'route.dashboard' } }, { path: '/article', name: 'article', component: () => import('../views/Article.vue'), meta: { requiresAuth: true, permission: 'route.article' } } ]; // 添加动态路由 function addRoutes(routes) { routes.forEach(route => { router.addRoute(route); }); } // 在登录成功后调用 // 假设从后端获取到 userRoutes login(this.username, this.password) .then(res => { // 登录成功 localStorage.setItem('userInfo', JSON.stringify(res)); // 存储用户信息 addRoutes(userRoutes); // 添加动态路由 this.$router.push('/dashboard'); // 跳转到仪表盘 }) .catch(err => { // 登录失败 alert(err); });
这种方式更加灵活,可以根据用户的角色动态生成路由表,避免了前端维护大量的路由配置。
三、 按钮权限:让用户只能点击他们能点击的按钮
按钮权限控制用户能否点击某个按钮。这可以通过自定义指令来实现。
-
自定义指令
v-permission
:创建一个自定义指令
v-permission
,用于判断用户是否有权限点击该按钮。// permission.js import Vue from 'vue' Vue.directive('permission', { inserted: function (el, binding) { const requiredPermission = binding.value; // 获取指令的值,即需要的权限 const userInfo = localStorage.getItem('userInfo'); const parsedUserInfo = userInfo ? JSON.parse(userInfo) : null; if (!parsedUserInfo || !parsedUserInfo.roles) { el.parentNode.removeChild(el); // 移除元素 return; } const userRoles = parsedUserInfo.roles; let hasPermission = false; userRoles.forEach(role => { if (rolesPermissions[role] && rolesPermissions[role].includes(requiredPermission)) { hasPermission = true; } }); if (!hasPermission) { el.parentNode.removeChild(el); // 移除元素 } } })
这个指令做了以下几件事:
- 获取指令的值,即需要的权限。
- 从 localStorage 中获取用户信息。
- 判断用户是否有相应的权限。
- 如果没有权限,移除该按钮。
-
在 Vue 组件中使用:
在 Vue 组件中使用
v-permission
指令。<template> <div> <button v-permission="'button.add'">添加</button> <button v-permission="'button.edit'">编辑</button> <button v-permission="'button.delete'">删除</button> </div> </template> <script> export default { name: 'MyComponent' } </script>
这样,只有拥有
button.add
权限的用户才能看到“添加”按钮,拥有button.edit
权限的用户才能看到“编辑”按钮,以此类推。注意: 这种方式只是隐藏了按钮,并不能阻止用户通过其他方式(例如直接调用接口)来执行操作。因此,后端也需要进行权限验证。
四、 数据权限:让用户只能看到他们能看到的数据
数据权限控制用户能看到哪些数据。这通常需要在后端实现。
-
后端权限验证:
后端需要根据用户的角色和权限,过滤掉用户无权访问的数据。
例如,假设有一个文章列表接口,后端需要根据用户的角色,只返回用户有权限访问的文章。
// Java 代码示例 (假设使用 Spring Security) @GetMapping("/articles") public List<Article> getArticles(Authentication authentication) { // 获取当前登录用户的角色 Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); List<String> roles = authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()); // 根据角色过滤文章 List<Article> articles = articleService.getAllArticles(); if (roles.contains("admin")) { // 管理员可以查看所有文章 return articles; } else if (roles.contains("editor")) { // 编辑只能查看自己编辑的文章 String username = authentication.getName(); return articles.stream().filter(article -> article.getAuthor().equals(username)).collect(Collectors.toList()); } else { // 其他用户只能查看公开的文章 return articles.stream().filter(Article::isPublic).collect(Collectors.toList()); } }
这段代码做了以下几件事:
- 获取当前登录用户的角色。
- 根据角色过滤文章。
- 管理员可以查看所有文章。
- 编辑只能查看自己编辑的文章。
- 其他用户只能查看公开的文章。
-
前端配合:
前端需要配合后端,将用户的角色信息发送到后端,以便后端进行权限验证。
// 获取文章列表 function getArticles() { const userInfo = localStorage.getItem('userInfo'); const parsedUserInfo = userInfo ? JSON.parse(userInfo) : null; const token = 'your_token'; // 从某个地方获取 token,例如登录成功后存储 return axios.get('/articles', { headers: { Authorization: `Bearer ${token}`, // 将 token 发送到后端 'X-User-Roles': parsedUserInfo ? parsedUserInfo.roles.join(',') : '' // 将角色信息发送到后端 } }); }
这段代码将用户的角色信息放在请求头中发送到后端。后端可以从请求头中获取角色信息,进行权限验证。
五、 权限管理最佳实践
- 前后端协同: 权限管理需要前后端协同配合。前端负责控制用户界面,后端负责控制数据访问。
- 最小权限原则: 给用户分配最小的权限,避免权限过度分配。
- 权限控制粒度: 权限控制的粒度要适中。粒度太粗,可能导致用户无法完成正常的操作;粒度太细,可能导致权限管理过于复杂。
- 权限数据维护: 权限数据(例如角色与权限的映射关系)需要进行维护。可以提供一个后台管理界面,方便管理员进行权限管理。
- 权限测试: 权限管理系统需要进行充分的测试,确保其安全可靠。
六、 代码示例总结 (简化版)
为了方便大家理解,这里提供一个简化版的代码示例,包含用户登录、路由权限和按钮权限。
<!DOCTYPE html>
<html>
<head>
<title>Vue 权限管理示例</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.js"></script>
<style>
body { font-family: sans-serif; }
.hidden { display: none; }
</style>
</head>
<body>
<div id="app">
<router-view></router-view>
</div>
<script>
// 角色权限定义
const rolesPermissions = {
'admin': ['route.dashboard', 'button.add'],
'editor': ['route.article', 'button.edit']
};
// 权限判断函数
function hasPermission(role, permission) {
return rolesPermissions[role] && rolesPermissions[role].includes(permission);
}
// 登录组件
const Login = {
template: `
<div>
<h2>登录</h2>
<input type="text" v-model="username" placeholder="用户名"><br><br>
<input type="password" v-model="password" placeholder="密码"><br><br>
<button @click="login">登录</button>
</div>
`,
data() {
return {
username: '',
password: ''
}
},
methods: {
login() {
// 模拟登录
if (this.username === 'admin' && this.password === 'password') {
localStorage.setItem('userRole', 'admin');
this.$router.push('/dashboard');
} else if (this.username === 'editor' && this.password === 'password') {
localStorage.setItem('userRole', 'editor');
this.$router.push('/article');
} else {
alert('用户名或密码错误');
}
}
}
};
// 仪表盘组件
const Dashboard = {
template: `
<div>
<h2>仪表盘 (Admin Only)</h2>
<button v-if="hasAddPermission">添加</button>
<button @click="logout">退出</button>
</div>
`,
computed: {
hasAddPermission() {
const userRole = localStorage.getItem('userRole');
return hasPermission(userRole, 'button.add');
}
},
methods: {
logout() {
localStorage.removeItem('userRole');
this.$router.push('/login');
}
}
};
// 文章组件
const Article = {
template: `
<div>
<h2>文章管理 (Editor Only)</h2>
<button v-if="hasEditPermission">编辑</button>
<button @click="logout">退出</button>
</div>
`,
computed: {
hasEditPermission() {
const userRole = localStorage.getItem('userRole');
return hasPermission(userRole, 'button.edit');
}
},
methods: {
logout() {
localStorage.removeItem('userRole');
this.$router.push('/login');
}
}
};
// 路由配置
const routes = [
{ path: '/login', component: Login },
{ path: '/dashboard', component: Dashboard, meta: { requiresAuth: true, permission: 'route.dashboard' } },
{ path: '/article', component: Article, meta: { requiresAuth: true, permission: 'route.article' } },
{ path: '/', redirect: '/login' }
];
const router = new VueRouter({
routes
});
// 路由守卫
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth) {
const userRole = localStorage.getItem('userRole');
if (!userRole) {
next('/login');
} else if (to.meta.permission && !hasPermission(userRole, to.meta.permission)) {
alert('没有权限');
next('/login');
} else {
next();
}
} else {
next();
}
});
// 创建 Vue 实例
new Vue({
router,
el: '#app'
});
</script>
</body>
</html>
这个例子展示了一个基本的权限管理流程,包括登录、路由权限和按钮权限。你可以根据自己的需要进行扩展和修改。
七、 总结
权限管理是一个复杂而重要的课题。希望通过今天的讲座,你对 Vue 项目中的权限管理有了更深入的理解。记住,安全第一,可维护性也很重要!
记住,代码只是工具,思想才是灵魂。灵活运用这些技巧,打造一个安全、可靠、易于维护的权限管理系统,让你的项目更上一层楼!
今天的讲座就到这里,感谢大家的聆听!祝大家编码愉快,bug 远离!