手把手教你如何使用Vue+Django实现RBAC权限管理

 更新时间:2026年03月10日 08:43:10   作者:幸福清风  
在开发一个复杂的Web应用时,权限管理是绕不开的核心环节,本文将和大家一起探索并实现一个基于角色权限管理系统,感兴趣的小伙伴可以跟随小编一起学习一下

前言

在开发一个复杂的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"表示只能查看nameage字段。
    • 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"),返回truefalse。在组件中,我们可以这样使用: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权限管理的资料请关注脚本之家其它相关文章!

相关文章

  • vue 动态修改a标签的样式的方法

    vue 动态修改a标签的样式的方法

    这篇文章主要介绍了vue 动态修改a标签的样式的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • vue实现实时上传文件进度条

    vue实现实时上传文件进度条

    这篇文章主要为大家详细介绍了vue实现实时上传文件进度条,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • vsCode中vue文件无法提示html标签的操作方法

    vsCode中vue文件无法提示html标签的操作方法

    在vsCode中书写Vue页面时无法提示,那真是很郁闷的事情,下面这篇文章主要给大家介绍了关于vsCode中vue文件无法提示html标签的操作方法,需要的朋友可以参考下
    2023-03-03
  • vue实现商品购物车全选反选

    vue实现商品购物车全选反选

    这篇文章主要为大家详细介绍了vue实现商品购物车全选反选,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • 探秘Vue异步更新机制中nextTick的原理与实现

    探秘Vue异步更新机制中nextTick的原理与实现

    nextTick 是 Vue 提供的一个重要工具,它的作用主要体现在帮助我们更好地处理异步操作,下面就跟随小编一起来探索一下nextTick的原理与实现吧
    2024-02-02
  • 详解vite如何支持cjs方案示例

    详解vite如何支持cjs方案示例

    这篇文章主要介绍了vite如何支持cjs方案示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • Vue.js设计与实现无限递归学习总结

    Vue.js设计与实现无限递归学习总结

    这篇文章主要为大家介绍了Vue.js设计与实现无限递归学习总结,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • vue-element-admin 菜单标签失效的解决方式

    vue-element-admin 菜单标签失效的解决方式

    今天小编就为大家分享一篇vue-element-admin 菜单标签失效的解决方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-11-11
  • Vue3中的模板语法和vue指令

    Vue3中的模板语法和vue指令

    这篇文章主要介绍了Vue3中的模板语法和vue指令,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-08-08
  • Vue中$once的两个实用小技巧分享

    Vue中$once的两个实用小技巧分享

    $once是一个函数,可以为Vue组件实例绑定一个自定义事件,但该事件只能被触发一次,触发之后随即被移除,下面这篇文章主要给大家介绍了关于Vue中$once的两个实用小技巧,需要的朋友可以参考下
    2022-04-04

最新评论