手把手教你如何使用Vue+Django实现RBAC权限管理
作者:幸福清风
前言
在开发一个复杂的Web应用时,权限管理是绕不开的核心环节。它决定了谁能看到什么页面、能对数据执行哪些操作。一个设计不佳的权限系统,轻则导致功能混乱,重则引发数据泄露等安全问题。
今天,我们将一起探索并实现一个基于角色(Role-Based Access Control, RBAC)的权限管理系统。我们将使用经典的Vue.js前端和Django后端技术栈,并提供一个完整、可运行的示例代码,让你能快速上手并应用到自己的项目中。
1. 权限模型设计:我们管理的是什么
我们的权限系统遵循一个简化的RBAC模型,其核心思想是将权限与“用户组”而非“用户”直接关联。
- 用户 (User):系统中的每一个使用者,拥有一个唯一的ID、用户名和所属的用户组(如“管理员”、“普通用户”、“学生”等)。
- 用户组 (User Group):一系列用户的集合,代表了一种角色。例如,“管理员”组的成员拥有最高权限。
- 权限 (Permission):定义了对某个特定资源(通常是后端的一个API接口或前端的一个页面路径)可以执行的操作。我们将其结构化为一个JSON对象,包含以下关键字段:
path: 资源路径,例如/api/user/table或/user/list。user_group: 该权限规则适用于哪个用户组。add,del,set,get: 四个布尔值(0或1),分别代表“增加”、“删除”、“修改”、“查询”的权限。field_add,field_set,field_get: 字符串,定义了该用户组在执行增、改、查操作时,可以访问哪些数据字段。例如,"field_get": "name,age"表示只能查看name和age字段。option: 一个JSON对象,用于存放更复杂的特殊权限,例如"examine": true可能代表拥有“审核”功能。
通过将这些权限规则存储在一张auth表中,我们可以实现高度灵活的动态权限配置。
2. 后端实现:Django中的权限校验
后端是权限控制的第一道也是最后一道防线。我们的Django后端主要通过一个中间件(Middleware)来实现权限校验。
核心逻辑如下:
- Token认证:用户登录成功后,服务器会返回一个加密的
token。后续的所有请求都需要在HTTP Header中携带这个x-auth-token。中间件首先解析这个token,从中获取用户ID,并查询出完整的用户信息(包括其用户组)。 - 权限缓存:为了提高性能,系统会将
auth表中的所有权限规则加载到内存中(一个全局字典dict_auth),避免每次请求都查询数据库。 - 权限匹配与校验:对于每一个请求,中间件都会提取其路径(
request.path)和请求方法(GET,POST等)。然后,它会根据当前用户的用户组和请求的路径,去缓存中查找对应的权限规则。最后,根据请求方法(如GET对应get权限)判断用户是否拥有执行此操作的权限。 - 默认规则:系统内置了一些默认规则。例如,如果找不到特定路径的权限配置,默认游客只能“查”,不能“增删改”。而“管理员”用户组则被硬编码为拥有所有路径的全部权限。
这种设计确保了只有经过身份验证且拥有足够权限的请求才能到达真正的业务视图函数,从而保护了核心数据和功能。
3. 前端实现:Vue中的动态权限响应
前端的权限控制主要是为了提供更好的用户体验。它可以根据用户的权限,动态地显示或隐藏页面元素(如按钮、菜单项),以及控制页面跳转。
我们通过一个Vue插件(permission.js)来封装权限逻辑:
$check_action(path, action): 这是一个核心方法。它接受一个路径和一个操作(如"get","add"),返回true或false。在组件中,我们可以这样使用:v-if="$check_action('/user/table', 'add')",来决定是否显示“添加用户”按钮。$check_field(action, field): 用于字段级别的权限控制。例如,在用户列表中,只有有权限的用户才能看到“邮箱”列。$get_auth(user_group): 在用户登录成功后,调用此方法向后端请求该用户组的全部权限列表,并将其存储在Vuex全局状态中,供整个应用随时使用。
通过这种方式,前端可以做到“千人千面”,只向用户展示他们有权操作的功能,避免了无效点击和潜在的错误。
4. 项目整体结构
首先,让我们来看一下整个项目的目录结构,这有助于理解各个模块的职责划分。
permission_demo/
├── backend/ (Django后端)
│ ├── manage.py
│ ├── requirements.txt
│ └── app/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── wsgi.py
│ ├── core/ # 核心工具类
│ │ └── base_service.py
│ ├── auth.py # 权限中间件
│ ├── views/
│ │ ├── __init__.py
│ │ ├── user.py # 用户登录、注册视图
│ │ └── auth.py # 权限数据管理视图
│ └── services/
│ ├── __init__.py
│ ├── user.py # 用户服务
│ ├── auth.py # 权限服务
│ └── access_token.py # Token服务
└── frontend/ (Vue前端)
├── public/
├── src/
│ ├── App.vue
│ ├── main.js
│ ├── router/
│ │ └── index.js
│ ├── store/
│ │ └── index.js
│ ├── plugins/
│ │ └── permission.js # 权限插件
│ └── views/
│ ├── Login.vue # 登录页
│ ├── UserList.vue # 用户列表页 (含权限控制)
│ └── AuthConfig.vue # 权限配置页 (管理员配置权限)
├── package.json
└── vue.config.js
关键文件解读:
- 后端
auth.py中间件:是整个权限系统的基石,负责请求拦截和校验。 - 前端
permission.js插件:是前端权限控制的入口,提供了便捷的API。 UserList.vue:一个绝佳的实践示例,展示了如何在列表页中根据权限动态显示“新增”、“编辑”、“删除”按钮和表格列。AuthConfig.vue:一个管理界面,允许管理员通过图形化界面配置复杂的权限规则,真正实现了“配置即代码”的灵活性。
5. 后端代码
a. 权限中间件 (backend/app/auth.py)
from django.utils.deprecation import MiddlewareMixin
from django.http import HttpResponse
from django.shortcuts import render
import json
import datetime
# 权限缓存
dict_auth = {}
class Auth:
def Check(self, user, path, method="get"):
"""检查用户权限"""
auth = self.Get_dict()
# 默认权限(游客)
model_auth = {
"user_group": "游客",
"path": path,
"add": 0,
"del": 0,
"set": 0,
"get": 1,
"field_add": "",
"field_set": "",
"field_get": "",
"option": {}
}
user_group = user["user_group"] if user else "游客"
# 从缓存获取权限
if (path in auth) and auth[path] and (user_group in auth[path]):
model_auth = auth[path][user_group]
else:
# 管理员拥有所有权限
if user_group == "管理员":
model_auth = {
"user_group": "管理员",
"path": path,
"add": 1,
"del": 1,
"set": 1,
"get": 1,
"field_add": "*",
"field_set": "*",
"field_get": "*",
"option": {"examine": True}
}
# 检查方法权限
if model_auth.get(method, 0):
return model_auth
return None
def Get_dict(self):
"""获取所有权限配置"""
if len(dict_auth.keys()) == 0:
from app.services.auth import Auth as AuthService
service = AuthService()
lst = service.Get_list({}, {"page": 0})
for o in lst:
path = o["path"]
if path not in dict_auth:
dict_auth[path] = {}
dict_auth[path][o["user_group"]] = o
return dict_auth
auth = Auth()
class AuthMiddleware(MiddlewareMixin):
"""权限验证中间件"""
def process_request(self, request):
# 跳过登录和静态资源
if request.path in ['/api/user/login', '/api/user/register', '/static/']:
return None
user = None
token = request.headers.get("x-auth-token")
# 验证 Token
if token:
user_id = request.session.get(token)
if not user_id:
from app.services.access_token import Access_token
obj = Access_token().Get_obj({"token": token})
if obj:
user_id = obj["user_id"]
request.session[token] = user_id
if user_id:
from app.services.user import User
user = User().Get_obj({"user_id": user_id})
request.user = user
# API 请求权限检查
if request.path.startswith('/api/'):
path = request.path.replace('/api/', '/').split('?')[0]
method = request.method.lower()
# 提取基础路径(如 /user/list)
model_auth = auth.Check(user, path, method)
if not model_auth:
return HttpResponse(json.dumps({"error": {"code": 403, "message": "权限不足"}}, ensure_ascii=False), content_type='application/json')
# 页面请求权限检查
else:
path = request.path
model_auth = auth.Check(user, path, "get")
if model_auth:
request.auth = model_auth
else:
return render(request, "403.html", {}, status=403)
return None
b. 核心工具类 (backend/app/core/base_service.py)
import pymysql
import json
import re
class Service:
def __init__(self, config):
self.table = config.get("table", "")
self.size = config.get("size", 30)
self.order = config.get("order", "desc")
self.sort = config.get("sort", "create_time")
self.where = config.get("where", {})
self.like = config.get("like", True)
self.fields = config.get("fields", "*")
self.conn = None
self.cursor = None
self.obj = None
self.error = None
self.sql = ""
self.count = 0
def connect(self):
try:
self.conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='root',
database='permission_demo',
charset='utf8'
)
self.cursor = self.conn.cursor(pymysql.cursors.DictCursor)
except Exception as e:
self.error = {"code": 500, "message": f"数据库连接失败: {str(e)}"}
def close(self):
if self.cursor:
self.cursor.close()
if self.conn:
self.conn.close()
def Get_list(self, where=None, other=None):
self.connect()
if self.error:
return []
# 构建 WHERE 条件
conditions = []
values = []
if self.where:
for key, value in self.where.items():
if isinstance(value, list):
placeholders = ','.join(['%s'] * len(value))
conditions.append(f"{key} IN ({placeholders})")
values.extend(value)
else:
conditions.append(f"{key} = %s")
values.append(value)
if where:
for key, value in where.items():
if isinstance(value, list):
placeholders = ','.join(['%s'] * len(value))
conditions.append(f"{key} IN ({placeholders})")
values.extend(value)
else:
conditions.append(f"{key} = %s")
values.append(value)
where_clause = "WHERE " + " AND ".join(conditions) if conditions else ""
# 分页
page = other.get("page", 0) if other else 0
offset = page * self.size
limit_clause = f"LIMIT {offset}, {self.size}" if page >= 0 else ""
# 排序
order_by = f"ORDER BY {self.sort} {self.order.upper()}"
self.sql = f"SELECT {self.fields} FROM {self.table} {where_clause} {order_by} {limit_clause}"
try:
self.cursor.execute(self.sql, values)
result = self.cursor.fetchall()
# 获取总数
count_sql = f"SELECT COUNT(*) as total FROM {self.table} {where_clause}"
self.cursor.execute(count_sql, values)
self.count = self.cursor.fetchone()["total"]
self.close()
return result
except Exception as e:
self.error = {"code": 500, "message": f"SQL执行错误: {str(e)}"}
self.close()
return []
def Get_obj(self, where, other=None):
self.connect()
if self.error:
return None
conditions = []
values = []
for key, value in where.items():
op = "="
if isinstance(value, str) and self.like:
op = "LIKE"
value = f"%{value}%"
conditions.append(f"{key} {op} %s")
values.append(value)
where_clause = "WHERE " + " AND ".join(conditions)
sql = f"SELECT {self.fields} FROM {self.table} {where_clause} LIMIT 1"
try:
self.cursor.execute(sql, values)
result = self.cursor.fetchone()
self.close()
return result
except Exception as e:
self.error = {"code": 500, "message": f"SQL执行错误: {str(e)}"}
self.close()
return None
def Add(self, data):
self.connect()
if self.error:
return False
columns = ', '.join(data.keys())
placeholders = ', '.join(['%s'] * len(data))
sql = f"INSERT INTO {self.table} ({columns}) VALUES ({placeholders})"
try:
self.cursor.execute(sql, list(data.values()))
self.conn.commit()
self.close()
return True
except Exception as e:
self.error = {"code": 500, "message": f"SQL执行错误: {str(e)}"}
self.close()
return False
def Set(self, data, where):
self.connect()
if self.error:
return False
set_clauses = ', '.join([f"{k} = %s" for k in data.keys()])
where_conditions = ' AND '.join([f"{k} = %s" for k in where.keys()])
sql = f"UPDATE {self.table} SET {set_clauses} WHERE {where_conditions}"
try:
self.cursor.execute(sql, list(data.values()) + list(where.values()))
self.conn.commit()
self.close()
return True
except Exception as e:
self.error = {"code": 500, "message": f"SQL执行错误: {str(e)}"}
self.close()
return False
def Del(self, where):
self.connect()
if self.error:
return False
where_conditions = ' AND '.join([f"{k} = %s" for k in where.keys()])
sql = f"DELETE FROM {self.table} WHERE {where_conditions}"
try:
self.cursor.execute(sql, list(where.values()))
self.conn.commit()
self.close()
return True
except Exception as e:
self.error = {"code": 500, "message": f"SQL执行错误: {str(e)}"}
self.close()
return False
c. 用户服务 (backend/app/services/user.py)
from app.core.base_service import Service
import hashlib
def md5hash(str):
return hashlib.md5(str.encode()).hexdigest()
class User(Service):
def __init__(self, *config):
config_temp = config[0] if config else {
"table": "user",
"size": 30,
}
super(User, self).__init__(config_temp)
def Login(self, body):
"""用户登录"""
username = body.get("username")
password = body.get("password")
if not username or not password:
return {"error": {"code": 70000, "message": "用户名和密码不能为空"}}
# 查询用户
obj = self.Get_obj({"username": username}, {"like": False})
if not obj:
return {"error": {"code": 70000, "message": "用户不存在"}}
# 验证密码
if obj["password"] != md5hash(password):
return {"error": {"code": 70000, "message": "密码错误"}}
# 检查用户状态
if obj["state"] != 1:
return {"error": {"code": 70000, "message": "账户不可用"}}
# 生成 Token
import time
timestamp = int(time.time()) * 1000
token = md5hash(str(obj["user_id"]) + "_" + str(timestamp))
# 存储 Token
from app.services.access_token import Access_token
Access_token().Add({
"token": token,
"user_id": obj["user_id"],
"maxage": 120 # 2 小时有效期
})
obj["token"] = token
return {"result": {"obj": obj}}
def Register(self, body):
"""用户注册"""
username = body.get("username")
password = body.get("password")
user_group = body.get("user_group", "普通用户")
if not username or not password:
return {"error": {"code": 70000, "message": "用户名和密码不能为空"}}
# 检查用户是否存在
if self.Get_obj({"username": username}, {"like": False}):
return {"error": {"code": 70000, "message": "用户名已存在"}}
# 添加用户
body["password"] = md5hash(password)
body["state"] = 1
result = self.Add(body)
if self.error:
return {"error": self.error}
return {"result": {"bl": True, "message": "注册成功"}}
d. 权限服务 (backend/app/services/auth.py)
from app.core.base_service import Service
class Auth(Service):
def __init__(self, *config):
config_temp = config[0] if config else {
"table": "auth",
"size": 30,
}
super(Auth, self).__init__(config_temp)
e. Token服务 (backend/app/services/access_token.py)
from app.core.base_service import Service
import datetime
class Access_token(Service):
def __init__(self, *config):
config_temp = config[0] if config else {
"table": "access_token",
"size": 30,
}
super(Access_token, self).__init__(config_temp)
def Get_obj(self, where):
"""获取有效Token"""
# 先清理过期token
expire_time = datetime.datetime.now() - datetime.timedelta(minutes=2) # 假设2小时过期
self.Del({"create_time": ("<", expire_time.strftime('%Y-%m-%d %H:%M:%S'))})
return super().Get_obj(where)
f. 用户视图 (backend/app/views/user.py)
from app.services.user import User
from app.services.auth import Auth
def State(ctx):
"""获取用户状态"""
user = ctx.request.user
if user:
auth_service = Auth()
permissions = auth_service.Get_list({"user_group": user["user_group"]})
user["permissions"] = permissions
return {"result": {"obj": user}}
return {"error": {"code": 401, "message": "未登录"}}
def Quit(ctx):
"""退出登录"""
token = ctx.request.headers.get("x-auth-token")
if token:
from app.services.access_token import Access_token
Access_token().Del({"token": token})
return {"result": {"message": "退出成功"}}
g. 权限视图 (backend/app/views/auth.py)
from app.services.auth import Auth
def Get_list(ctx):
"""获取权限列表"""
where = {}
if "user_group" in ctx.request.GET:
where["user_group"] = ctx.request.GET["user_group"]
service = Auth()
lst = service.Get_list(where, ctx.request.GET.dict())
return {"result": {"list": lst, "count": service.count}}
h. Settings配置 (backend/app/settings.py)
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'app.auth.AuthMiddleware', # 自定义权限中间件
]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'permission_demo',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '127.0.0.1',
'PORT': '3306',
}
}
6. 前端代码
a. 权限插件 (frontend/src/plugins/permission.js)
import store from '@/store'
export default {
install(Vue) {
// 检查路径操作权限
Vue.prototype.$check_action = function(path, action = "get") {
const power = this.$get_power(path)
if (power && power[action] !== 0 && power[action] !== false) {
return true
}
return false
}
// 获取权限对象
Vue.prototype.$get_power = function(path) {
const list = store.state.web.auth
for (let i = 0; i < list.length; i++) {
if (list[i].path === path) {
return list[i]
}
}
return null
}
// 检查字段权限
Vue.prototype.$check_field = function(action, field) {
const power = this.$get_power(this.$route.path)
if (power) {
const auth = power[`field_${action}`]
if (auth && auth !== '*') {
return auth.split(',').includes(field)
}
return auth === '*'
}
return false
}
// 检查特殊选项权限
Vue.prototype.$check_option = function(path, option) {
const power = this.$get_power(path)
if (power && power.option) {
return !!power.option[option]
}
return false
}
// 获取用户组权限
Vue.prototype.$get_auth = function(user_group = "游客", callback) {
if (!user_group) user_group = "游客"
this.$get("~/api/auth/get_list?", { user_group }, (json) => {
store.commit("set_auth", [])
if (json.result && json.result.list) {
store.commit("set_auth", json.result.list)
if (callback) callback()
} else if (json.error) {
console.error(json.error)
}
})
}
}
}
b. 用户状态Store (frontend/src/store/index.js)
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
web: {
user: {},
auth: [],
}
},
mutations: {
set_user(state, user) {
state.web.user = user
},
set_auth(state, auth) {
state.web.auth = auth
}
},
actions: {
},
modules: {
}
})
c. 登录页面 (frontend/src/views/Login.vue)
<template>
<div class="login-container">
<el-form :model="form" :rules="rules" ref="loginForm" label-width="80px" class="login-form">
<h2>用户登录</h2>
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username" placeholder="请输入用户名"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="form.password" type="password" placeholder="请输入密码"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">登录</el-button>
<el-button @click="onRegister">注册</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'Login',
data() {
return {
form: {
username: '',
password: ''
},
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' }
]
}
}
},
methods: {
onSubmit() {
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.$post('~/api/user/login?', this.form, (res) => {
if (res.result) {
const user = res.result.obj
this.$store.commit('set_user', user)
localStorage.setItem('token', user.token)
// 获取权限
this.$get_auth(user.user_group, () => {
this.$router.push({ name: 'UserList' })
})
} else {
this.$message.error(res.error.message)
}
})
}
})
},
onRegister() {
this.$post('~/api/user/register?', this.form, (res) => {
if (res.result) {
this.$message.success(res.result.message)
} else {
this.$message.error(res.error.message)
}
})
}
}
}
</script>
<style scoped>
.login-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f2f5;
}
.login-form {
width: 400px;
padding: 30px;
background: white;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
</style>
d. 用户列表页面 (frontend/src/views/UserList.vue)
<template>
<div class="user-list">
<el-card>
<div slot="header">
<span>用户管理</span>
<el-button
v-if="$check_action('/user/table','add')"
type="primary"
@click="handleAdd">
添加用户
</el-button>
</div>
<el-table :data="list" v-loading="loading">
<el-table-column prop="user_id" label="ID" width="80"></el-table-column>
<el-table-column prop="username" label="用户名"></el-table-column>
<el-table-column prop="nickname" label="昵称"></el-table-column>
<!-- 根据字段权限动态显示列 -->
<el-table-column prop="email" label="邮箱" v-if="$check_field('get', 'email')"></el-table-column>
<el-table-column prop="user_group" label="用户组"></el-table-column>
<el-table-column prop="state" label="状态" width="80">
<template slot-scope="scope">
<el-tag :type="scope.row.state === 1 ? 'success' : 'danger'">
{{ scope.row.state === 1 ? '正常' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="250" fixed="right">
<template slot-scope="scope">
<el-button
v-if="$check_action('/user/table','get')"
type="text"
@click="handleView(scope.row)">
查看
</el-button>
<el-button
v-if="$check_action('/user/table','set')"
type="text"
@click="handleEdit(scope.row)">
编辑
</el-button>
<el-button
v-if="$check_action('/user/table','del')"
type="text"
style="color: red"
@click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
<script>
export default {
name: 'UserList',
data() {
return {
loading: false,
list: []
}
},
created() {
this.loadData()
},
methods: {
loadData() {
this.loading = true
this.$get("~/api/user/get_list?", {}, (res) => {
this.loading = false
if (res.result) {
this.list = res.result.list
}
})
},
handleAdd() {
this.$message.info('触发添加操作')
// 实现添加逻辑
},
handleEdit(row) {
this.$message.info(`编辑用户: ${row.username}`)
// 实现编辑逻辑
},
handleDelete(row) {
this.$confirm(`确定要删除用户 "${row.username}" 吗?`)
.then(() => {
// 实现删除逻辑
this.$message.success('删除成功')
this.loadData()
})
},
handleView(row) {
this.$message.info(`查看用户: ${row.username}`)
// 实现查看逻辑
}
}
}
</script>
<style scoped>
.user-list {
padding: 20px;
}
</style>
e. 权限配置页面 (frontend/src/views/AuthConfig.vue)
<template>
<div class="auth-config">
<el-card>
<div slot="header">
<span>权限配置</span>
</div>
<el-form :model="form" inline>
<el-form-item label="用户组">
<el-select v-model="form.user_group" placeholder="请选择用户组">
<el-option label="管理员" value="管理员"></el-option>
<el-option label="普通用户" value="普通用户"></el-option>
<el-option label="游客" value="游客"></el-option>
</el-select>
</el-form-item>
<el-form-item label="路径">
<el-input v-model="form.path" placeholder="/user/list"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadPermissions">查询</el-button>
</el-form-item>
</el-form>
<el-table :data="permissions" v-loading="loading">
<el-table-column prop="path" label="路径"></el-table-column>
<el-table-column prop="user_group" label="用户组"></el-table-column>
<el-table-column label="操作权限">
<template slot-scope="scope">
<el-checkbox v-model="scope.row.add" :true-label="1" :false-label="0">新增</el-checkbox>
<el-checkbox v-model="scope.row.del" :true-label="1" :false-label="0">删除</el-checkbox>
<el-checkbox v-model="scope.row.set" :true-label="1" :false-label="0">修改</el-checkbox>
<el-checkbox v-model="scope.row.get" :true-label="1" :false-label="0">查询</el-checkbox>
</template>
</el-table-column>
<el-table-column label="字段权限">
<template slot-scope="scope">
<el-input size="mini" v-model="scope.row.field_add" placeholder="add fields"></el-input>
<el-input size="mini" v-model="scope.row.field_set" placeholder="set fields"></el-input>
<el-input size="mini" v-model="scope.row.field_get" placeholder="get fields"></el-input>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button size="mini" @click="updatePermission(scope.row)">保存</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
<script>
export default {
name: 'AuthConfig',
data() {
return {
loading: false,
form: {
user_group: '',
path: ''
},
permissions: []
}
},
methods: {
loadPermissions() {
if (!this.form.user_group || !this.form.path) {
this.$message.warning('请先选择用户组和路径')
return
}
this.loading = true
this.$get('~/api/auth/get_list?', this.form, (res) => {
this.loading = false
if (res.result) {
this.permissions = res.result.list
}
})
},
updatePermission(row) {
this.$post('~/api/auth/set?', row, (res) => {
if (res.result) {
this.$message.success('权限更新成功')
} else {
this.$message.error(res.error.message)
}
})
}
}
}
</script>
<style scoped>
.auth-config {
padding: 20px;
}
</style>
f. Main.js入口文件 (frontend/src/main.js)
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import permission from './plugins/permission'
import axios from 'axios'
Vue.use(ElementUI)
Vue.use(permission)
// 配置 axios
Vue.prototype.$axios = axios.create({
baseURL: 'http://127.0.0.1:8000',
timeout: 10000
})
// 请求拦截器,自动携带token
Vue.prototype.$axios.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers['x-auth-token'] = token
}
return config
})
// 简化 HTTP 方法
Vue.prototype.$get = function(url, params, callback) {
this.$axios.get(url, { params }).then(res => {
if (callback) callback(res.data)
})
}
Vue.prototype.$post = function(url, data, callback) {
this.$axios.post(url, data).then(res => {
if (callback) callback(res.data)
})
}
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
7. 数据库设计
最后,是支撑整个权限系统的基础——数据库表结构。
-- 创建数据库
CREATE DATABASE IF NOT EXISTS permission_demo CHARACTER SET utf8 COLLATE utf8_general_ci;
USE permission_demo;
-- 用户表
CREATE TABLE `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(32) NOT NULL,
`nickname` varchar(50) DEFAULT NULL,
`avatar` varchar(255) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`user_group` varchar(50) DEFAULT '普通用户',
`state` tinyint(1) DEFAULT 1,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 权限表
CREATE TABLE `auth` (
`auth_id` int(11) NOT NULL AUTO_INCREMENT,
`user_group` varchar(50) NOT NULL,
`mod_name` varchar(100) DEFAULT NULL,
`table_name` varchar(100) DEFAULT NULL,
`path` varchar(200) NOT NULL,
`add` tinyint(1) DEFAULT 0,
`del` tinyint(1) DEFAULT 0,
`set` tinyint(1) DEFAULT 0,
`get` tinyint(1) DEFAULT 1,
`field_add` text,
`field_set` text,
`field_get` text,
`option` text,
PRIMARY KEY (`auth_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Token表
CREATE TABLE `access_token` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`token` varchar(32) NOT NULL,
`user_id` int(11) NOT NULL,
`maxage` int(11) DEFAULT 120,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 插入初始权限数据示例
INSERT INTO `auth` (`user_group`, `path`, `add`, `del`, `set`, `get`, `field_add`, `field_set`, `field_get`, `option`) VALUES
('管理员', '/user/table', 1, 1, 1, 1, '*', '*', '*', '{}'),
('普通用户', '/user/table', 0, 0, 0, 1, '', '', 'user_id,username,nickname', '{}');
8. 总结
本文从概念到实践,带你走了一遍完整的权限管理系统的设计与实现过程。我们看到了一个成熟系统应有的样子:后端严格把关,前端优化体验,两者配合无间。
这套方案的核心价值在于其灵活性和可维护性。通过将权限规则外化到数据库,业务人员可以在不改动代码的情况下,轻松调整系统权限,这对于快速迭代的项目来说至关重要。
以上就是手把手教你如何使用Vue+Django实现RBAC权限管理的详细内容,更多关于Vue Django RBAC权限管理的资料请关注脚本之家其它相关文章!
