vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue Django RBAC权限管理

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

作者:幸福清风

在开发一个复杂的Web应用时,权限管理是绕不开的核心环节,本文将和大家一起探索并实现一个基于角色权限管理系统,感兴趣的小伙伴可以跟随小编一起学习一下

前言

在开发一个复杂的Web应用时,权限管理是绕不开的核心环节。它决定了谁能看到什么页面、能对数据执行哪些操作。一个设计不佳的权限系统,轻则导致功能混乱,重则引发数据泄露等安全问题。

今天,我们将一起探索并实现一个基于角色(Role-Based Access Control, RBAC)的权限管理系统。我们将使用经典的Vue.js前端Django后端技术栈,并提供一个完整、可运行的示例代码,让你能快速上手并应用到自己的项目中。

1. 权限模型设计:我们管理的是什么

我们的权限系统遵循一个简化的RBAC模型,其核心思想是将权限与“用户组”而非“用户”直接关联。

通过将这些权限规则存储在一张auth表中,我们可以实现高度灵活的动态权限配置。

2. 后端实现:Django中的权限校验

后端是权限控制的第一道也是最后一道防线。我们的Django后端主要通过一个中间件(Middleware)来实现权限校验。

核心逻辑如下:

这种设计确保了只有经过身份验证且拥有足够权限的请求才能到达真正的业务视图函数,从而保护了核心数据和功能。

3. 前端实现:Vue中的动态权限响应

前端的权限控制主要是为了提供更好的用户体验。它可以根据用户的权限,动态地显示或隐藏页面元素(如按钮、菜单项),以及控制页面跳转。

我们通过一个Vue插件(permission.js)来封装权限逻辑:

通过这种方式,前端可以做到“千人千面”,只向用户展示他们有权操作的功能,避免了无效点击和潜在的错误。

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

关键文件解读:

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权限管理的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文