给账号设置页面权限
1.数据库设计:
2.后端实现:
3.前端实现:
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.json
1. 数据库设计
创建用户、角色和权限相关的表:
-- 用户表,存储用户的基本信息
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. 后端实现
用户认证和权限管理:
authController.js
const 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.js
const 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.js
const 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.js
const 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.js
const 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.js
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'your_database_name',
});
module.exports = pool;
authMiddleware.js
const 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.前端实现
状态管理和路由权限控制:
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.js
import { 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.js
import { 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项目中,页面的显示跟路由有关,你可以给指定的账号配置不同的页面权限,然后在用户登录的时候拿到用户的角色信息,通过角色获取到对应的页面权限,然后用页面权限匹配用户路由信息,最后返还给前端,前端拿到的就是符合账号权限的路由信息,最后在动态添加路由渲染成页面。
10 回答10.7k 阅读
3 回答11.4k 阅读✓ 已解决
2 回答11.4k 阅读✓ 已解决
2 回答13.3k 阅读✓ 已解决
5 回答4.4k 阅读✓ 已解决
2 回答11.2k 阅读✓ 已解决
2 回答8.1k 阅读✓ 已解决
用户登录->获取token->获取用户信息及配置的路由数据->根据数据添加页面路由、生成按钮权限数据->进入路由中的第一个页面