django-rest-framework搭建平台实战教程四-使用fast-crud集成后端权限功能

FastCrud (简称fs) 是基于Vue3的面向配置的crud开发框架,快速开发crud功能,可作为低代码平台的基础框架。

安装并启动项目

参考http://fast-crud.docmirror.cn/guide/start/demo.html

我用的是fs-admin-element

.env文件修改

参考http://fast-crud.docmirror.cn/admin/#_2-%E4%BF%AE%E6%94%B9%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6

修改后端ip和端口

参考http://fast-crud.docmirror.cn/admin/#_3-%E4%BF%AE%E6%94%B9%E5%90%8E%E7%AB%AFip%E5%92%8C%E7%AB%AF%E5%8F%A3

禁用mock

参考http://fast-crud.docmirror.cn/admin/#_4%E3%80%81%E7%A6%81%E7%94%A8mock

修改登录接口

参考http://fast-crud.docmirror.cn/admin/#_6-%E4%BF%AE%E6%94%B9%E7%99%BB%E5%BD%95%E6%8E%A5%E5%8F%A3

django后端urls.py路由全部加上api/

router = routers.DefaultRouter()
router.register(r'api/users', views.DUserViewSet)
router.register(r'api/groups', views.GroupViewSet)
router.register(r'api/permission', views.PermissionViewSet)
urlpatterns = [
    path('', include(router.urls)),
    # path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
    # path('admin/', admin.site.urls),
    path('api/register',views.register),
    path('api/login',views.login),
    path('api/sys/authority/user/mine',views.info)
]

修改/src/store/modules/user.ts

...
setToken(info: string, expire: number) {
this.token = "Bearer " + info; LocalStorage.set(TOKEN_KEY, this.token, expire); },

 正常就能登录系统了

角色管理页前后端对接

view/sys/authority/role/api.ts匹配后端接口

import { request } from "/src/api/service";
const apiPrefix = "/groups/";
export async function GetList(query) {
  return request({
    url: apiPrefix,
    method: "get",
    data: query
  });
}

export async function AddObj(obj) {
  return request({
    url: apiPrefix,
    method: "post",
    data: obj
  });
}

export async function UpdateObj(obj,id) {
  return request({
    url: apiPrefix+id+'/',
    method: "put",
    data: obj
  });
}

export async function DelObj(id) {
  return request({
    url: apiPrefix+id+'/',
    method: "delete",
    params: { id }
  });
}

crud.ts改接口参数

...
      columns: {
        id: {
          title: "id",
          form: { show: false }, // 表单配置
          column: {
            width: 70,
            sorter: true
          }
        },
        name: {
          title: "角色名称",
          type: "text",
          search: { show: true },
          form: {
            rules: [
              { required: true, message: "请输入角色名称" },
              { max: 50, message: "最大50个字符" }
            ]
          }, // 表单配置
          column: {
            sorter: true
          }
        },
        update_time: {
          title: "更新时间",
          type: "datetime",
          column: {
            sorter: true
          },
          form: { show: false } // 表单配置
        }
      }

/src/plugin/fast-crud/index.ts修改默认的创建时间字段

后端authen添加custompagination.py

from rest_framework import pagination
from rest_framework.response import Response


class CustomPagination(pagination.PageNumberPagination):
    def get_paginated_response(self, data):
        return Response({
            'currentPage': self.page.number,
            'pageSize': self.page_size,
            'total': self.page.paginator.count,
            'records': data
        })

修改settings

...
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'djangoXfastcrud.authen.CustomPagination.CustomPagination',
    'PAGE_SIZE': 10,
'DEFAULT_AUTHENTICATION_CLASSES': (
    'rest_framework_simplejwt.authentication.JWTAuthentication',
),
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}

前端src/plugins/fast_crud/index.tsx修改全局翻页参数

...
transformQuery: ({ page, form, sort }) => { return { page: page.currentPage, ...form }; },

后端GroupViewSet添加显示权限tree视图

...
@action(['POST'], detail=False, permission_classes=[permissions.IsAuthenticated])
    def tree(self, request: Request):
        content_types = ContentType.objects.all()
        resdata = []
        for c in content_types:
            permissions = Permission.objects.filter(content_type_id=c.id).all()
            plist = []
            for p in permissions:
                plist.append({
                    "id": p.id,
                    "parentId": c.id,
                    "permission": p.codename,
                    "title": p.name,
                })
            resdata.append({
                "id": -1,
                "parentId": -1,
                "permission": c.app_label,
                "title": c.model,
                "children": plist
            })
        return Response({
            "code": 0,
            "data": resdata
        })

前端api.ts添加接口

export async function GetTree() {
  return request({
    url: apiPrefix + "tree/",
    method: "post"
  });
}

修改index.vue

async function authzOpen(roleId) {
    permissionTreeData.value = await api.GetTree(); #改成新的接口
...

添加授权相关接口

class GroupViewSet(CustomViewSet):
...

    @action(methods=['POST'], detail=False, permission_classes=[permissions.IsAuthenticated])
    def getPermissionIds(self, request: Request):
        group = Group.objects.filter(id=request.query_params['id']).first()
        ids = group.permissions.values_list("id", flat=True)
        return Response({
            "code": 0,
            "data": ids
        })

    @action(methods=['POST'], detail=False)
    def authz(self, request: Request):
        if request.user.has_perm("auth.change_group"):
            group = Group.objects.filter(id=request.data['roleId']).first()
            group.permissions.set(request.data['permissionIds'])
            return Response({"code": 0, "message": "success"})
        else:
            raise PermissionDenied

前端api.ts修改url

/**
 * 获取角色权限资源
 * @param roleId
 * @returns {*}
 * @constructor
 */
export function getPermissionIds(roleId) {
  return request({
    url: apiPrefix + "getPermissionIds/",
    method: "post",
    params: { id: roleId }
  });
}

/**
 * 授权
 * @param roleId
 * @param permissionIds
 * @returns {*}
 * @constructor
 */
export function DoAuthz(roleId, permissionIds) {
  return request({
    url: apiPrefix + "/authz/",
    method: "post",
    data: { roleId, permissionIds }
  });
}

角色页面所有功能完成

同样思路修改用户页面

api.ts

import { request } from "/src/api/service";
const apiPrefix = "/users/";
export async function GetList(query) {
  return request({
    url: apiPrefix,
    method: "get",
    data: query
  });
}

export async function AddObj(obj) {
  return request({
    url: apiPrefix,
    method: "post",
    data: obj
  });
}

export async function UpdateObj(obj) {
    delete obj._create_time;
    delete obj.update_time;
return request({
    url: apiPrefix+obj.id+'/',
    method: "put",
    data: obj
  });
}

export async function DelObj(id) {
  return request({
    url: apiPrefix+id+'/',
    method: "delete",
    params: { id }
  });
}

export async function GetObj(id) {
  return request({
    url: apiPrefix + "/info",
    method: "post",
    params: { id }
  });
}

crud.ts

...
columns: {
        id: {
          title: "id",
          form: { show: false }, // 表单配置
          column: {
            width: 70,
            sorter: true
          }
        },
        username: {
          title: "用户名",
          type: "text",
          search: { show: true }, // 开启查询
          form: {
            rules: [
              { required: true, message: "请输入用户名" },
              { max: 50, message: "最大50个字符" }
            ]
          },
          editForm: { component: { disabled: true } },
          column: {
            sorter: true
          }
        },
        password: {
          title: "密码",
          type: "text",
          key: "password",
          column: {
            show: false
          },
          form: {
            rules: [{ max: 50, message: "最大50个字符" }],
            component: {
              showPassword: true
            },
            helper: "填写则修改密码"
          },
          editForm: { show: false }
        },
        nickName: {
          title: "昵称",
          type: "text",
          search: { show: true }, // 开启查询
          form: {
            rules: [{ max: 50, message: "最大50个字符" }]
          },
          column: {
            sorter: true
          }
        },
        avatar: {
          title: "头像",
          type: "cropper-uploader",
          column: {
            width: 100,
            component: {
              //设置高度,修复操作列错位的问题
              style: {
                height: "30px",
                width: "auto"
              }
            }
          }
        },
        remark: {
          title: "备注",
          type: "text",
          column: {
            sorter: true
          },
          form: {
            rules: [{ max: 100, message: "最大100个字符" }]
          }
        },
        groups: {
          title: "角色",
          type: "dict-select",
          dict: dict({
            url: "/groups/get_groups_dict/",
            value: "id",
            label: "name"
          }), // 数据字典
          search: {
              show: true
          },
          form: {
            component: { multiple: true }
          },
          column: {
            width: 250,
            sortable: true
          }
        },
        update_time: {
          title: "修改时间",
          type: "datetime",
          form: { show: false }, // 表单配置
          column: {
            sortable: "update_time",
            width: 180
          }
        },
      }

角色选择框接口

class GroupViewSet(CustomViewSet):
...
    @action(['POST'], detail=False, permission_classes=[permissions.IsAuthenticated])
    def get_groups_dict(self, request: Request):
        obj = DGroup.objects.values("id", "name")
        return Response({
            "code": 0,
            "data": obj
        })

角色页面所有功能完成

最后解决登录获取权限的报错接口

添加视图

...
@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def getuserpermissions(request: Request):
    user: User = request.user
    plist = []
    permissions = user.get_group_permissions()
    for p in permissions:
        plist.append({"permission":p})
    return Response({
        "code": 0,
        "data": plist
    })

urls.py添加下

path('api/sys/authority/user/permissions',views.getuserpermissions)

超级用户登录获取全部权限

添加页面权限

import LayoutPass from "/@/layout/layout-pass.vue";

export const sysResources = [
  {
    title: "系统管理",
    name: "sys",
    path: "/sys",
    redirect: "/sys/authority",
    component: LayoutPass,
    meta: {
      icon: "ion:settings-outline",
      permission: "sys.sys"
    },
    children: [
      {
        title: "权限管理",
        name: "authority",
        path: "/sys/authority",
        redirect: "/sys/authority/permission",
        meta: {
          icon: "ion:ribbon-outline",
          //需要校验权限
          permission: "sys.auth"
        },
        children: [
          {
            title: "权限资源管理",
            name: "permission",
            meta: {
              icon: "ion:list-outline",
              //需要校验权限
              permission: "sys.per"
            },
            path: "/sys/authority/permission",
            component: "/sys/authority/permission/index.vue"
          },
          {
            title: "角色管理",
            name: "role",
            meta: {
              icon: "ion:people-outline",
              permission: "sys.role"
            },
            path: "/sys/authority/role",
            component: "/sys/authority/role/index.vue"
          }
        ]
      },
      {
        title: "用户管理",
        name: "user",
        meta: {
          icon: "ion:person-outline",
          permission: "sys.user"
        },
        path: "/sys/authority/user",
        component: "/sys/authority/user/index.vue"
      }
    ]
  }
];

index.vue修改权限资源管理页面方便加权限

const permission = ref({
      add: hasPermissions("sys:auth:per:add"),
      edit: hasPermissions("sys:auth:per:edit"),
      remove: hasPermissions("sys:auth:per:remove")
    });

改成

const permission = ref({
      add: hasPermissions("auth.add_permission"),
      edit: hasPermissions("auth.change_permission"),
      remove: hasPermissions("auth.delete_permission")
    });

PermissionViewSet添加相关方法

    def create(self, request, *args, **kwargs):
        request.data["content_type"] = request.data["parentId"]
        request.data["name"] = request.data["title"]
        request.data["codename"] = request.data["permission"]
        return super().create(request, *args, **kwargs)

    @action(['POST'], detail=False, permission_classes=[permissions.IsAuthenticated])
    def tree(self, request: Request):
        content_types = ContentType.objects.all()
        resdata = []
        for c in content_types:
            permissions = Permission.objects.filter(content_type_id=c.id).all()
            plist = []
            for p in permissions:
                plist.append({
                    "id": p.id,
                    "parentId": c.id,
                    "permission": p.codename,
                    "title": p.name,
                    "sort": 100
                })
            resdata.append({
                "id": c.id,
                "parentId": -1,
                "permission": c.app_label,
                "title": c.model,
                "children": plist,
                "sort": 100
            })
        return Response({
            "code": 0,
            "data": resdata
        })

api.ts

import { request } from "/src/api/service";
const apiPrefix = "/permissions/";
export async function GetList(query) {
  return request({
    url: apiPrefix,
    method: "get",
    data: query
  });
}

export async function GetTree() {
  return request({
    url: apiPrefix + "tree/",
    method: "post"
  });
}

export async function AddObj(obj) {
  return request({
    url: apiPrefix,
    method: "post",
    data: obj
  });
}

export async function UpdateObj(obj) {
  return request({
    url: apiPrefix,
    method: "put",
    data: obj
  });
}

export async function DelObj(id) {
  return request({
    url: apiPrefix,
    method: "delete",
    params: { id }
  });
}

export async function GetObj(id) {
  return request({
    url: apiPrefix + "/info",
    method: "post",
    params: { id }
  });
}

crud.ts修改url

/sys/authority/permission/tree
改成
/permissions/tree/

这样权限就能添加了。

superuser授予一个用户这些权限用户就能浏览菜单,去除后会提示“对不起,您没有权限”

这样用户权限前后端联调完成。

 

 

 

posted @ 2023-12-24 00:44  紧肛胡撸娃  阅读(1060)  评论(0编辑  收藏  举报