思路1.数据库设计:设计用户、角色和权限相关的表结构。2.后端实现:使用 Node.js 和 Express 实现用户认证和权限管理。3.前端实现:使用 Vue3 和 Pinia 实现状态管理和路由权限控制。4.组件权限控制:使用自定义指令或方法在组件层面控制权限。目录结构project-root/ ├── backend/ │ ├── controllers/ │ │ └── authController.js │ ├── models/ │ │ ├── user.js │ │ ├── role.js │ │ └── permission.js │ ├── routes/ │ │ └── authRoutes.js │ ├── config/ │ │ └── db.js │ ├── middleware/ │ │ └── authMiddleware.js │ └── server.js ├── frontend/ │ ├── src/ │ │ ├── components/ │ │ │ └── UserButton.vue │ │ ├── views/ │ │ │ ├── Home.vue │ │ │ ├── Login.vue │ │ │ └── Denied.vue │ │ ├── store/ │ │ │ └── userPermissions.js │ │ ├── router/ │ │ │ └── index.js │ │ ├── App.vue │ │ └── main.js └── package.json1. 数据库设计创建用户、角色和权限相关的表:-- 用户表,存储用户的基本信息 CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, -- 用户ID,主键,自增 username VARCHAR(255) NOT NULL, -- 用户名,非空 password VARCHAR(255) NOT NULL, -- 密码,非空 role_id INT, -- 角色ID,外键 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间,默认当前时间 updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -- 更新时间,默认当前时间,更新时自动更新 ); -- 角色表,存储不同的角色信息 CREATE TABLE roles ( id INT AUTO_INCREMENT PRIMARY KEY, -- 角色ID,主键,自增 name VARCHAR(255) NOT NULL, -- 角色名称,非空 description VARCHAR(255), -- 角色描述 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间,默认当前时间 updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -- 更新时间,默认当前时间,更新时自动更新 ); -- 权限表,存储具体的权限信息 CREATE TABLE permissions ( id INT AUTO_INCREMENT PRIMARY KEY, -- 权限ID,主键,自增 name VARCHAR(255) NOT NULL, -- 权限名称,非空 description VARCHAR(255), -- 权限描述 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间,默认当前时间 updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -- 更新时间,默认当前时间,更新时自动更新 ); -- 角色权限关联表,存储角色与权限的对应关系 CREATE TABLE role_permissions ( role_id INT, -- 角色ID,外键 permission_id INT, -- 权限ID,外键 PRIMARY KEY (role_id, permission_id), -- 联合主键 FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE, -- 角色ID外键,级联删除 FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE -- 权限ID外键,级联删除 ); -- 用户权限关联表,存储用户与权限的直接对应关系(可选) CREATE TABLE user_permissions ( user_id INT, -- 用户ID,外键 permission_id INT, -- 权限ID,外键 PRIMARY KEY (user_id, permission_id), -- 联合主键 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, -- 用户ID外键,级联删除 FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE -- 权限ID外键,级联删除 ); 2. 后端实现用户认证和权限管理:在 controllers/authController.js 中实现登录和权限检查逻辑。在 routes/authRoutes.js 中定义相关路由。在 middleware/authMiddleware.js 中实现权限中间件。authController.jsconst bcrypt = require('bcrypt'); const jwt = require('jsonwebtoken'); const { getUserByUsername, createUser } = require('../models/user'); const { getRoleById } = require('../models/role'); const { getPermissionsByRoleId } = require('../models/permission'); const login = async (req, res) => { const { username, password } = req.body; const user = await getUserByUsername(username); if (!user) { return res.status(401).json({ message: 'Invalid username or password' }); } const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) { return res.status(401).json({ message: 'Invalid username or password' }); } const role = await getRoleById(user.role_id); const permissions = await getPermissionsByRoleId(user.role_id); const token = jwt.sign({ id: user.id, role: role.name, permissions: permissions.map(p => p.name) }, 'your_jwt_secret', { expiresIn: '1h' }); res.json({ token }); }; const register = async (req, res) => { const { username, password, role_id } = req.body; const hashedPassword = await bcrypt.hash(password, 10); const userId = await createUser(username, hashedPassword, role_id); res.status(201).json({ id: userId }); }; module.exports = { login, register }; user.jsconst pool = require('../config/db'); const getUserByUsername = async (username) => { const [rows] = await pool.query('SELECT * FROM users WHERE username = ?', [username]); return rows[0]; }; const createUser = async (username, password, role_id) => { const [result] = await pool.query('INSERT INTO users (username, password, role_id) VALUES (?, ?, ?)', [username, password, role_id]); return result.insertId; }; module.exports = { getUserByUsername, createUser }; role.jsconst pool = require('../config/db'); const getRoleById = async (id) => { const [rows] = await pool.query('SELECT * FROM roles WHERE id = ?', [id]); return rows[0]; }; module.exports = { getRoleById }; permission.jsconst pool = require('../config/db'); const getPermissionsByRoleId = async (role_id) => { const [rows] = await pool.query('SELECT p.* FROM permissions p JOIN role_permissions rp ON p.id = rp.permission_id WHERE rp.role_id = ?', [role_id]); return rows; }; module.exports = { getPermissionsByRoleId }; authRoutes.jsconst express = require('express'); const { login, register } = require('../controllers/authController'); const router = express.Router(); router.post('/login', login); router.post('/register', register); module.exports = router; db.jsconst mysql = require('mysql2/promise'); const pool = mysql.createPool({ host: 'localhost', user: 'root', password: 'password', database: 'your_database_name', }); module.exports = pool; authMiddleware.jsconst jwt = require('jsonwebtoken'); const authenticate = (req, res, next) => { const token = req.header('Authorization').replace('Bearer ', ''); if (!token) { return res.status(401).json({ message: 'Access denied' }); } try { const decoded = jwt.verify(token, 'your_jwt_secret'); req.user = decoded; next(); } catch (err) { res.status(401).json({ message: 'Invalid token' }); } }; const authorize = (requiredPermissions) => { return (req, res, next) => { const userPermissions = req.user.permissions; const hasPermission = requiredPermissions.every(permission => userPermissions.includes(permission)); if (!hasPermission) { return res.status(403).json({ message: 'Forbidden' }); } next(); }; }; module.exports = { authenticate, authorize }; server.js:const express = require('express'); const bodyParser = require('body-parser'); const authRoutes = require('./routes/authRoutes'); const app = express(); app.use(bodyParser.json()); app.use('/api/auth', authRoutes); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); }); 3.前端实现状态管理和路由权限控制:在 store/userPermissions.js 中存储用户角色和权限信息。在 router/index.js 中使用路由守卫检查权限。UserButton.vue<template> <div> <el-button v-if="hasPermission('sys:user:add')" type="success" plain>添加用户</el-button> </div> </template> <script> import { userPermissionsStore } from '@/store/userPermissions'; import { storeToRefs } from 'pinia'; export default { setup() { const store = userPermissionsStore(); const { userPermissions } = storeToRefs(store); const hasPermission = (permission) => { return userPermissions.value.includes(permission); }; return { hasPermission }; }, }; </script> Home.vue<template> <div> <h1>Home Page</h1> <UserButton /> </div> </template> <script> import UserButton from '@/components/UserButton.vue'; export default { components: { UserButton, }, }; </script> Login.vue<template> <div> <h1>Login Page</h1> <form @submit.prevent="login"> <input v-model="username" placeholder="Username" /> <input v-model="password" type="password" placeholder="Password" /> <button type="submit">Login</button> </form> </div> </template> <script> import { userPermissionsStore } from '@/store/userPermissions'; import { useRouter } from 'vue-router'; export default { data() { return { username: '', password: '', }; }, setup() { const store = userPermissionsStore(); const router = useRouter(); const login = async () => { // 模拟登录请求 const response = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: this.username, password: this.password }), }); const data = await response.json(); if (response.ok) { store.setUserPermissions(data); router.push('/home'); } else { alert(data.message); } }; return { login }; }, }; </script> Denied.vue<template> <div> <h1>Access Denied</h1> <p>You do not have permission to view this page.</p> </div> </template> userPermissions.jsimport { defineStore } from 'pinia'; import { ref } from 'vue'; export const userPermissionsStore = defineStore('userPermissions', () => { const roles = ref(''); const userPermissions = ref([]); const isLogin = ref(false); const setUserPermissions = (params) => { userPermissions.value = params.permissions; roles.value = params.role; isLogin.value = true; }; const logout = () => { userPermissions.value = []; roles.value = ''; isLogin.value = false; }; return { isLogin, userPermissions, roles, setUserPermissions, logout }; }); index.jsimport { createRouter, createWebHashHistory } from 'vue-router'; import { userPermissionsStore } from '@/store/userPermissions'; import { storeToRefs } from 'pinia'; const routes = [ { path: '/home', name: 'home', component: () => import('../views/Home.vue'), meta: { requireAuth: true, roles: ['admin', 'guest'] }, }, { path: '/login', name: 'login', component: () => import('../views/Login.vue') }, { path: '/denied', name: 'denied', component: () => import('../views/Denied.vue') }, ]; const router = createRouter({ history: createWebHashHistory(), routes, }); router.beforeEach((to, from, next) => { const store = userPermissionsStore(); const { isLogin, roles } = storeToRefs(store); if (to.meta.requireAuth) { if (isLogin.value) { if (to.meta.roles.includes(roles.value)) { next(); } else { next('/denied'); } } else { next('/login'); } } else { next(); } }); export default router; App.vue<template> <router-view /> </template> main.js:import { createApp } from 'vue'; import { createPinia } from 'pinia'; import App from './App.vue'; import router from './router'; const app = createApp(App); app.use(createPinia()); app.use(router); app.mount('#app'); package.json{ "name": "vue3-nodejs-auth", "version": "1.0.0", "description": "A project with Vue3 and Node.js for user authentication and authorization", "main": "backend/server.js", "scripts": { "start": "node backend/server.js", "dev": "concurrently \"npm run dev:backend\" \"npm run dev:frontend\"", "dev:backend": "nodemon backend/server.js", "dev:frontend": "cd frontend && npm run serve" }, "dependencies": { "bcrypt": "^5.0.1", "body-parser": "^1.19.0", "concurrently": "^6.2.1", "express": "^4.17.1", "jsonwebtoken": "^8.5.1", "mysql2": "^2.2.5", "pinia": "^2.0.11", "vue": "^3.2.31", "vue-router": "^4.0.12" }, "devDependencies": { "nodemon": "^2.0.7" } }
vue项目中,页面的显示跟路由有关,你可以给指定的账号配置不同的页面权限,然后在用户登录的时候拿到用户的角色信息,通过角色获取到对应的页面权限,然后用页面权限匹配用户路由信息,最后返还给前端,前端拿到的就是符合账号权限的路由信息,最后在动态添加路由渲染成页面。
思路
1.数据库设计:
2.后端实现:
3.前端实现:
4.组件权限控制:
目录结构
1. 数据库设计
创建用户、角色和权限相关的表:
2. 后端实现
用户认证和权限管理:
authController.js
user.js
role.js
permission.js
authRoutes.js
db.js
authMiddleware.js
server.js:
3.前端实现
状态管理和路由权限控制:
UserButton.vue
Home.vue
Login.vue
Denied.vue
userPermissions.js
index.js
App.vue
main.js:
package.json