首页IT科技react dva前端框架(react 高效高质量搭建后台系统 系列 —— 前端权限)

react dva前端框架(react 高效高质量搭建后台系统 系列 —— 前端权限)

时间2025-06-20 15:20:04分类IT科技浏览4174
导读:其他章节请看:...

其他章节请看:

react 高效高质量搭建后台系统 系列

权限

本系列已近尾声             ,权限是后台系统必不可少的一部分                    ,本篇首先分析spug项目中权限的实现      ,最后在将权限加入到我们的项目中来             。

spug 中权限的分析

权限示例

比如我要将应用发布模块的查看权限分给某用户(例如 pjl)       ,可以这样操作:

在角色管理中新建一角色(例如 demo)                    ,然后给该角色配置权限:

新建用户(pjl)并赋予其 demo 权限

pjl 登录后就只能看到自己有权限的页面和操作:

入口

上述示例中             ,以 pjl 登录成功后返回如下数据:

{ "data": { "id": 2, "access_token": "74b0fe67d09646ee9ca44fc48c6b457a", "nickname": "pjl", "is_supper": false, "has_real_ip": true, "permissions": [ "deploy.app.view", "deploy.repository.view" ] }, "error": "" }

其中 permissions 表示该用户的权限                    。

vscode 搜索 deploy.app.view 有两处:

发布配置(也是“应用管理              ”)页面入口 index.js      。如果没有查看权限(deploy.app.view)就不展示该页面内容       。 // spug\src\pages\deploy\app\index.js export default observer(function () { return ( <AuthDiv auth="deploy.app.view"> <Breadcrumb> <Breadcrumb.Item>首页</Breadcrumb.Item> <Breadcrumb.Item>应用发布</Breadcrumb.Item> <Breadcrumb.Item>应用管理</Breadcrumb.Item> </Breadcrumb> <ComTable/> </AuthDiv> ); })

Tip:AuthDiv 用于控制页内组件的权限       ,里面通过 hasPermission 判断是否有对应的权限                    ,如果没有             ,该组件内的子元素则不会显示                    。

export default function AuthDiv(props) { let disabled = props.disabled === undefined ? false : props.disabled; if (props.auth && !hasPermission(props.auth)) { disabled = true; } return disabled ? null : <div {...props}>{props.children}</div> } // 前端页面的权限判断(仅作为前端功能展示的控制,具体权限控制应在后端实现) export function hasPermission(strCode) { const {isSuper, permissions} = Permission; if (!strCode || isSuper) return true; for (let or_item of strCode.split(|)) { if (isSubArray(permissions, or_item.split(&))) { return true } } return false } routes.js             。里面定义的就是菜单(详见左侧导航)       。auth指授权                    ,这里定义的是模块和页面的查看权限                    。其中deploy.app.view|deploy.repository.view|deploy.request.view表示只要有其中一个权限                    ,“应用发布                   ”就会展示             。 // spug\src\routes.js { icon: <FlagOutlined />, title: 应用发布, auth: deploy.app.view|deploy.repository.view|deploy.request.view, child: [ { title: 应用管理, auth: deploy.app.view, path: /deploy/app, component: DeployApp }, { title: 构建仓库, auth: deploy.repository.view, path: /deploy/repository, component: DeployRepository }, { title: 发布申请, auth: deploy.request.view, path: /deploy/request, component: DeployRequest }, ] }, 页面级的权限

现在我们已经知道页面级的权限解决思路:

在定义菜单的地方(routes.js)通过 auth 定义查看(spug 中是 xx.xx.view)权限。如果没有 auth 则说明该页面任何人都可以访问 组装菜单时仅取出有权限的菜单页面 // spug\src\layout\Sider.js function makeMenu(menu) { // 仅取出有权限的菜单 if (menu.auth && !hasPermission(menu.auth)) return null; if (!menu.title) return null; return menu.child ? _makeSubMenu(menu) : _makeItem(menu) }

如果直接通过 url 去访问没有权限的页面会如何:

直接 404                    。

为什么是 404?因为路由数组(Routes)中只取了有权限的菜单页面                    。

// spug\src\layout\index.js <Switch> {/* 路由数组。里面每项类似这样:<Route exact key={route.path} path=/home component={HomeComponent}/> */} {Routes} {/* 没有匹配则进入 NotFound */} <Route component={NotFound}/> </Switch>

Routes 的内容请看路由初始化方法:

// 将 routes 中有权限的路由提取到 Routes 中 function initRoutes(Routes, routes) { for (let route of routes) { if (route.component) { // 如果不需要权限,或有权限则放入 Routes if (!route.auth || hasPermission(route.auth)) { Routes.push(<Route exact key={route.path} path={route.path} component={route.component}/>) } } else if (route.child) { initRoutes(Routes, route.child) } } } 页内级权限

新增              、删除等页内的权限如何实现的呢?我们把发布配置页内的新建权限放开             ,后端权限返回列表中将有: deploy.app.add

vscode 只搜索到一处匹配deploy.app.add             。

<TableCard tKey="da" ... actions={[ <AuthButton auth="deploy.app.add" type="primary" icon={<PlusOutlined/>} onClick={() => store.showForm()}>新建</AuthButton> ]}

其中 AuthButton 和上文的 AuthDiv 一样                    ,如果有权限则显示其中内容

// spug\src\components\AuthButton.js export default function AuthButton(props) { let disabled = props.disabled; if (props.auth && !hasPermission(props.auth)) { disabled = true; } return disabled ? null : <Button {...props}>{props.children}</Button> }

于是我们知道页内级的权限解决思路:

在功能权限设置中定义对应操作权限的值      ,选中后把该值传递给后端

权限的值在 codes.js 中定义如下:

// codes.js { key: deploy, label: 应用发布, pages: [ { key: app, label: 应用管理, perms: [ { key: view, label: 查看应用 }, { key: add, label: 新建应用 }, { key: edit, label: 编辑应用 }, { key: del, label: 删除应用 }, { key: config, label: 查看配置 }, ] }, { key: repository, label: 构建仓库, perms: [ { key: view, label: 查看构建 }, { key: add, label: 新建版本 }, ] }, ... ] }, 通过 AuthDiv                   、AuthButton等组件的 auth 匹配             ,如果没有权限则不展示该组件内容                    。 isSuper

登录后返回权限中还有个字段 is_supper      。数据格式就像这样:

{ "data": { "id": 1, "access_token": "6a0cef69a7d64b6f8c755ff341266e76", "nickname": "管理员", "is_supper": true, "has_real_ip": true, "permissions": [ ] }, "error": "" }

is_supper 是 true                    ,permissions 为空数组             。猜测 is_supper 的作用有2个:

一个用处(vscode 搜索 isSuper)直接返回有权限      ,无需其他判断                    。就像这样: // 前端页面的权限判断(仅作为前端功能展示的控制       ,具体权限控制应在后端实现) export function hasPermission(strCode) { const {isSuper, permissions} = Permission; if (!strCode || isSuper) return true; for (let or_item of strCode.split(|)) { if (isSubArray(permissions, or_item.split(&))) { return true } } return false } 一个用处(vscode 搜索 is_supper)是作为最大的权限再做一些页面上的操作      。例如这里隐藏该片段: <Form.Item hidden={store.record.is_supper} label="角色" style={{marginBottom: 0}}> <Form.Item name="role_ids" style={{display: inline-block, width: 80%}} extra="权限最大化原则                    ,组合多个角色权限       。"> <Select mode="multiple" placeholder="请选择"> {roleStore.records.map(item => ( <Select.Option value={item.id} key={item.id}>{item.name}</Select.Option> ))} </Select> </Form.Item> <Form.Item style={{display: inline-block, width: 20%, textAlign: right}}> <Link to="/system/role">新建角色</Link> </Form.Item> </Form.Item>

myspug 中权限的实现

需求

spug 中的权限整体思路:

通过一个配置页面(功能权限设置弹框)将某些权限(页面级和页内级)赋予某角色             ,再让某用户属于该角色 用户登录成功后返回数据(permissions      、is_supper字段)告诉前端该用户的权限 前端在组装菜单和路由时只取有权限的       ,就这样解决了页面级的权限问题 再通过封装的 AuthDiv              、AuthButton 等组件嵌套页内级权限                    ,例如新建             ,如果没有权限则不显示

所以权限这里包含两条线:

一条是用户通过配置页面,将权限赋予某用户                    ,保存在后端数据库 另一条是用户登录后                    ,根据后端返回的权限显示有权限查看的页面和操作

需求:用户没有以下三个权限(报警联系人                    、报警联系组      、角色管理中的新增)                    。见下图3个红框:

实现效果

最终效果如下:

代码实现 页面级权限配置

在 routes.js 中定义每个菜单(也是路由)的权限:

export default [ // 无需权限 { icon: <DesktopOutlined />, title: 工作台, path: /home, component: HomeIndex }, { icon: <AlertOutlined />, title: 报警中心, auth: alarm.alarm.view|alarm.contact.view|alarm.group.view, child: [ { title: 报警历史, auth: alarm.alarm.view, path: /alarm/alarm, component: AlarmCenter }, { title: 报警联系人, auth: alarm.contact.view, path: /alarm/contact, component: AlarmCenter }, { title: 报警联系组, auth: alarm.group.view, path: /alarm/group, component: AlarmCenter }, ] }, { icon: <AlertOutlined />, title: 系统管理, auth: "system.role.view", child: [ { title: 角色管理, auth: system.role.view, path: /system/role, component: SystemRole }, ] }, { path: /welcome/info, component: WelcomeInfo }, ]

Tip:例如角色管理页面需要system.role.view这个权限             。命名任意,因为后端返回的权限是前端发送给的后端       。

hasPermission

更新权限判断逻辑                    。之前统统返回 true:

// 之前 export function hasPermission(strCode) { return true }

现在看后端是否返回该权限:

// 前端页面的权限判断(仅作为前端功能展示的控制             ,具体权限控制应在后端实现) export function hasPermission(strCode) { const { isSuper, permissions } = Permission; if (!strCode || isSuper) return true; for (let or_item of strCode.split(|)) { if (isSubArray(permissions, or_item.split(&))) { return true } } return false } // 数组包含关系判断 export function isSubArray(parent, child) { for (let item of child) { if (!parent.includes(item.trim())) { return false } } return true } 页内级权限组件

定义两个组件(AuthButton       、AuthDiv)                    ,并导出:

import React from react; import { Button } from antd; import { hasPermission } from @/libs; export default function AuthButton(props) { let disabled = props.disabled; if (props.auth && !hasPermission(props.auth)) { disabled = true; } return disabled ? null : <Button {...props}>{props.children}</Button> } import React from react; import { hasPermission } from @/libs; export default function AuthDiv(props) { let disabled = props.disabled === undefined ? false : props.disabled; if (props.auth && !hasPermission(props.auth)) { disabled = true; } return disabled ? null : <div {...props}>{props.children}</div> } // myspug\src\compoments\index.js import NotFound from ./NotFound; import TableCard from ./TableCard; import AuthButton from ./AuthButton import AuthDiv from ./AuthDiv export { NotFound, TableCard, AuthButton, AuthDiv, }

例如新建按钮就放入 AuthButton 组件中      ,而整个角色管理的入口模块就放入 AuthDiv 中             。

组装 mock登录后的数据: // mock/index.js { "data": { "id": 2, "access_token": "74b0fe67d09646ee9ca44fc48c6b457a", "nickname": "pjl", "is_supper": false, "has_real_ip": true, "permissions": ["system.role.view", "alarm.alarm.view"] }, "error": "" } 角色管理入口页放入 AuthDiv: // myspug\src\pages\system\role\index.js export default function () { return ( <AuthDiv auth="system.role.view"> <ComTable /> </AuthDiv> ) } 新增按钮放入 AuthButton: // myspug\src\pages\system\role\Table.js <TableCard rowKey="id" title="角色列表" actions={[ <AuthButton type="primary" icon={<PlusOutlined/>} auth="system.role.add" >新增</AuthButton> ]}

扩展

spug 中权限的缺点

spug 中权限虽然简单             ,其中一个缺点是每开发一个新模块                    ,就得更新权限配置页面(即功能权限设置)和路由(routes.js)配置:

如果将其改为可配置      ,将减小这部分的工作。

其他章节请看:

react 高效高质量搭建后台系统 系列

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
uniapp部署h5(uniapp 开发H5打包微信小程序样式失效的解决之道)