1.组件简介

该版本开发速度较快,项目业务内容固定的场景

  • 用户的角色固定
  • 角色对应的权限固定

 

2. 设计思路

  • 确定所有的角色:管理员,财务,审核员
  • 确定每个角色具备的权限

2.1 前端

  • 登录,选择角色登录,vuex中保存用户的角色和token
  • 根据登录的角色,动态加载菜单
    • 在路由中定义is_menu和role字段,来判断该路由是否为菜单和能显示的角色
    •     {
              path: '/front',
              name: 'Front',
              component: () => import('../views/FrontView'),
              meta:{
                  is_menu:true,
                  roles:["manager", "admin"]
              }
          },
      
       
  • 用户是否访问某一个页面
    • 通过路由的导航守卫来实现
    • 基于vuex中的角色。判断该角色是否能访问对应的路由
  • 页面中的局部功能,如增删改查
    • 按钮的展示:根据vuex中的角色判断是否展示v-if
      <el-button v-if="xxx">新建</el-button>
    • 功能: 角色只有有对应的权限,才允许发送ajax请求,我们也可以进行角色权限的判定
      if(has_permission("admin", "manager")){
        
      }else{
        
      }
        

2.2 后端

  • 用户登录,生成token返回
  • 再次请求,根据认证组件,判定token,是否登陆成功
  • 权限的校验,根据当前登录的角色,和token中的权限进行判断
    • 我们可以在每一个视图函数中判断权限
    • 也可以使用drf中的权限组件进行判断 + 配置文件中的权限配置
  • api的实现

3. 项目的实现

3.1 前端

  页面展示效果

 

 

 

  部分后端数据通过模拟实现

 

  • 登录页面的实现

    <template>
      <div class="my_div">
        <el-form
            label-width="100px"
            :model="state.form"
            style="max-width: 460px"
        >
          <el-form-item label="角色">
            <el-select v-model="state.form.role">
              <el-option :key="item.title" :value="item.value" :label="item.title" v-for="item in state.options"/>
            </el-select>
          </el-form-item>
          <el-form-item label="用户名">
            <el-input v-model="state.form.username"/>
          </el-form-item>
          <el-form-item label="密码">
            <el-input v-model="state.form.password" type="password"/>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="onSubmit" style="width: 200px;">登录</el-button>
          </el-form-item>
        </el-form>
      </div>
    </template>
    
    <script setup>
    import {reactive} from "vue";
    import {useStore} from "vuex";
    import {useRouter} from "vue-router";
    
    const router = useRouter()
    const store = useStore()
    const state = reactive({
      form: {
        role: "管理员",
        username: "cisco",
        password: "123"
      },
      options: [
        {title: "管理员", value: "manager"},
        {title: "财务", value: "caiwu"},
        {title: "运维", value: "yunwei"},
      ]
    })
    
    
    function onSubmit() {
    //  1. 向后端发送登录请求
    //  2. 接收后端响应数据
      const context = {
        role: state.form.role,
        token: "9a81a00f-fe88-4c4b-ad2d-68cd17dceb4a"
      }
    
    //  3. 保存在vuex中
      store.commit("login", context)
    //  4.页面跳转
      router.push({name: "basic"})
    }
    </script>
    
    <style scoped>
    .my_div {
      width: 500px;
      height: 200px;
      border: 1px solid green;
      margin-top: 200px;
      margin-left: auto;
      margin-right: auto;
      padding: 20px;
    }
    </style>
    

     

  • vuex的实现,用于保存用户登录数据
    import {createStore} from 'vuex'
    
    export default createStore({
        state: {
            token: localStorage.getItem("token") || "",
            role: localStorage.getItem("role") || "",
        },
        getters: {},
        mutations: {
            login(state, {token, role}) {
                state.token = token
                state.role = role
    
                localStorage.setItem("token", token)
                localStorage.setItem("role", role)
            }
        },
        actions: {},
        modules: {}
    })
    

     

  • 路由的编写
    • 确定路由是否为菜单
    • 确定路由能够访问的角色
    • 菜单名字
    • 路由导航守卫的编写
      import {createRouter, createWebHistory} from 'vue-router'
      import store from "@/store";
      
      
      const routes = [
          {
              path: '/login',
              name: 'login',
              component: () => import('../views/LoginView.vue')
          },
          {
              path: '/admin',
              name: 'admin',
              component: () => import('../views/AdminView.vue'),
              children: [
                  {
                      path: 'basic',
                      name: 'basic',
                      component: () => import('../views/admin/BasicView.vue'),
                      meta: {
                          role: ["manager", "caiwu", "yunwei"],
                          is_menu: true,
                          title: "基本信息",
                      }
                  },
                  {
                      path: 'user',
                      name: 'user',
                      component: () => import('../views/admin/UserView.vue'),
                      meta: {
                          role: ["manager", "yunwei"],
                          is_menu: true,
                          title: "用户管理",
      
                      }
                  },
                  {
                      path: 'role',
                      name: 'role',
                      component: () => import('../views/admin/RoleView.vue'),
                      meta: {
                          role: ["manager", "caiwu"],
                          is_menu: true,
                          title: "角色管理"
                      }
                  }
              ]
          }
      ]
      
      const router = createRouter({
          history: createWebHistory(process.env.BASE_URL),
          routes
      })
      
      router.beforeEach((to, from, next) => {
          console.log(to)
          if (to.meta.role) {
              const role = store.state.role
              console.log(role)
              if (to.meta.role.indexOf(role) === -1) {
                  router.push({name: "login"})
              } else {
                  next()
              }
          } else {
              next()
          }
      })
      export default router
      

       

       
  •  按钮的展示
    • 添加,删除和编辑三个按钮同过角色来进行确定是否渲染在页面
    • 自定义has_permission函数
      <template>
        <el-card class="box-card">
          <template #header>
            <div class="card-header">
              <span>基本信息</span>
              <el-button class="button" type="success" v-if="hasPermission(['manager'])">添加</el-button>
            </div>
          </template>
          <div>
            <el-table :data="state.tableData" style="width: 100%">
              <el-table-column prop="name" label="姓名" width="180"/>
              <el-table-column prop="age" label="年龄" width="180"/>
              <el-table-column prop="address" label="住址"/>
              <el-table-column label="操作">
                <el-button class="button" type="primary" v-if="hasPermission(['manager', 'caiwu'])">编辑</el-button>
                <el-button class="button" type="danger" v-if="hasPermission(['manager', 'yunwe'])">删除</el-button>
              </el-table-column>
            </el-table>
          </div>
        </el-card>
      </template>
      
      <script setup>
      import {reactive} from "vue";
      import store from "@/store";
      
      const state = reactive({
        tableData: [
          {
            name: "张三",
            age: 16,
            address: "上海市"
          }
        ]
      })
      
      function hasPermission(roleList) {
        const role = store.state.role
        if (roleList.indexOf(role) !== -1) {
          return true
        }
      
      }
      </script>
      
      <style scoped>
      .card-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      </style>
      

       

    前端就可以动态的显示菜单和按钮

  • 管理员身份

     

     

  • 运维身份

     

     

3.2 后端

假设有一个学生表,我们对学生表进行操作

  • 模型类
    from django.db import models
    
    
    # Create your models here.
    
    class User(models.Model):
        username = models.CharField(verbose_name="用户名", max_length=32)
        password = models.CharField(verbose_name="密码", max_length=32)
    
        role_choice = ((1, "admin"), (2, "caiwu"), (3, "yunwei"))
        role = models.SmallIntegerField(verbose_name="角色", choices=role_choice, default=1)
    
    
    class Student(models.Model):
        username = models.CharField(verbose_name="用户名", max_length=32)
        age = models.SmallIntegerField(verbose_name="年龄")
        address = models.CharField(verbose_name="住址", max_length=32)
    

     

  • 路由
    from django.contrib import admin
    from django.urls import path
    from rest_framework import routers
    from api import views
    
    router = routers.SimpleRouter()
    router.register("api/student", viewset=views.StudentView, basename="student")
    
    urlpatterns = [
        path('admin/', admin.site.urls),
    ]
    urlpatterns += router.urls
    
  • 认证
    class User:
    def __init__(self, name=None, role=None):
    self.name = name
    self.role = role


    class MineAuthentication(BaseAuthentication): def authenticate(self, request): token = request.query_params.get("token") if not token: raise AuthenticationFailed("认证失败") role = request.query_params.get("role") user = User("xxx", role) return (user, token) def authenticate_header(self, request): return "API"

     

  • 权限
    class MinePermission(BasePermission):
        def has_permission(self, request, view):
            """
            判断角色是否有对应的权限
            :param request:
            :param view:
            :return:
            """
            role = request.user.role
            permission_dict = settings.PERMISSIONS.get(role)
            request_method = request.method
            request_name = request_method.reslover_match.url_name
            if not permission_dict:
                return False
            method_list = permission_dict[request_name]
            if method_list and request_method in method_list:
                return True
            return False
    

     

  • 视图
    class StudentView(ModelViewSet):
        queryset = models.Student.objects.all()
        serializer_class = StudentModelSerializers
        authentication_classes = [MineAuthentication, ]
        permission_classes = [MinePermission, ]
    

     

  • 配置文件
    PERMISSIONS = {
        "manager": {
            "student-detail": ["GET", "POST"],
            "student-list": ["GET", "POST", "PATCH", "DELETE", "PUT"],
        },
        "caiwu": {
            "student-detail": ["GET", "POST"],
        },
        "yunwei": {
            "student-detail": ["GET", "POST"],
            "student-list": ["GET", ]
        }
    }
    REST_FRAMEWORK = {
        "UNAUTHENTICATED_USER": None,
        "UNAUTHENTICATED_TOKEN": None,
    }
    

     

 

4. 扩展

上面的案例中前端是根据角色来判定用户是否有对应的权限,我们也可以按照权限来判定,修改如下即可

  • 用户登陆成功之后,需要返回当前用户的所有权限,保存在vuex中
  • 在需要判定是否有权限的地方,根据权限来判定
    has_permission({depart-detail:["GET","POST"])

     

posted on 2023-03-05 15:38  阿明明  阅读(30)  评论(0编辑  收藏  举报