回顾
接上篇,上次我们在前端项目中,补全了前端的在线HTTP测试,并且完善了一些功能。要知道,咱们这是一个接口测试平台。一个接口自动化平台,当然要有不同的用户权限和对应的菜单模块。因此这篇文章我们讲讲解用户权限和动态菜单的相关教程,和BUG。也在此发出求助,希望有人能针对文章最后的BUG,提出一些修改建议!感谢大家!
目标
- 后端新增用户权限相关
- 前端编写路由权限,实现动态菜单
- BUG描述
为何要有多个用户角色?
- 安全性和数据保护: 平台可能涉及到敏感信息和数据,不同的用户可能需要不同的权限级别来保护数据的安全性。只有授权的用户能够访问特定的数据和功能,从而防止未经授权的访问和数据泄露。
- 业务流程管理: 在一个企业内部的平台中,不同的部门和角色可能涉及到不同的业务流程和操作。例如,销售团队可能需要处理订单和客户信息,而财务团队可能需要访问财务数据和报表。根据不同的业务流程,用户权限需要进行相应的限制。
- 功能定制和个性化体验: 不同的用户可能对平台的需求和使用方式有所不同。有些用户可能只需要基本的功能,而有些用户可能需要更高级的功能。通过分配不同的权限,可以定制每个用户的体验,使其只能访问他们关心的功能。
- 组织结构和角色: 在一个组织中,不同的角色可能对平台有不同的需求。例如,管理层可能需要查看业务数据的汇总报表,而操作人员可能需要执行日常的数据输入和处理。通过设置不同的权限,可以适应不同的组织结构和角色需求。
- 遵循法规和政策: 某些行业和地区可能有特定的法规和政策要求,要求限制对某些数据和功能的访问。通过设置权限,可以确保平台的操作符合相关法规和政策要求。
动态菜单的好处?
- 安全性: 前端动态菜单可以根据用户的权限和角色来生成可见的菜单选项。这意味着用户只能看到他们被授权访问的页面和功能,从而提高了系统的安全性。未经授权的用户无法看到和访问不属于他们的菜单项,减少了数据泄露和恶意访问的风险。
- 用户体验: 前端动态菜单可以根据用户的角色和权限动态展示相关的功能选项,从而简化用户界面,减少不必要的复杂性。用户只会看到与其工作职责相关的菜单,提高了用户体验和工作效率。
- 可维护性: 前端动态菜单通常是基于角色和权限配置进行生成的,这使得系统的维护变得更加简单。当角色、权限或菜单项发生变化时,只需要更新配置,而无需修改大量的页面代码。
- 扩展性: 如果系统需要新增功能模块或页面,只需在菜单配置中添加相应的权限即可。新的功能将根据配置自动显示在菜单中,无需对现有代码进行大规模修改。
- 适应性: 前端动态菜单可以适应不同用户的需求和角色。不同的用户可以看到他们所需的不同菜单选项,从而实现个性化的用户界面。
- 统一性: 前端动态菜单可以统一管理和配置系统中的菜单项,确保菜单的一致性和统一性。这样可以避免不同开发人员在不同页面中创建不同的菜单,导致用户界面的混乱。
- 便捷性: 前端动态菜单可以根据用户的权限自动隐藏不可访问的功能选项,减少用户因权限问题而产生的困惑。用户只会看到他们有权限使用的功能,不会被无关的选项干扰。
如何设计?
- 权限控制: 不同角色的用户可能拥有不同的功能权限,例如管理员可以进行用户管理等操作,普通用户只能访问一般的功能页面。通过在菜单配置中设置不同的权限,可以确保只有具有相应权限的用户才能看到和访问相关菜单项。
- 界面简洁性: 不同角色的用户关注点和工作职责不同,他们不需要看到所有的菜单项。通过根据角色隐藏一些不相关的菜单,可以简化用户界面,减少不必要的干扰,提升用户体验。
- 系统安全性: 隐藏不相关的菜单可以减少未经授权用户的误操作和访问,从而提高系统的安全性。例如,某些敏感操作只应该由管理员进行,一般用户不应该看到相关菜单。
- 可维护性: 在权限配置的基础上,可以更灵活地管理不同角色的功能访问。当角色的权限需求变化时,只需调整相应的权限配置,而不需要修改页面代码。
- 扩展性: 如果未来需要新增一些功能模块,可以直接在菜单配置中添加相应的权限配置。这样新增功能会自动在合适的角色下显示,无需额外修改代码。
因此,综上所述,出于产品设计的出发点,我决定暂时拿掉前端Login页面中的注册功能,Login页面就只有登录功能,后期想要创建用户,必须要超级管理员在用户管理中新增,其他人无法自行注册。因此,我们还需要后端在初始化表的时候,初始化一个超级管理员admin。
后端新增用户权限
- 权限相关字段role
- 初始化超级管理员admin
权限相关字段role
- User表中,新增role字段与默认参数role=0
- UserDto结构体中,新增role字段
至于其他部分,根据提示修修改改即可,不举例处理
初始化超级管理员admin
编辑src/app/dao/__init__.py文件
import asyncio
from datetime import datetime
from loguru import logger
from config import AdminConfig
from src.app.middleware.my_jwt import AbandonJWT
from src.app.models import Base, engine, async_session
from src.app.models.user import User
from sqlalchemy import or_, select, func # 导入or_、select和func类/函数,用于构建SQL查询语句
async def init_user():
"""
初始化注册用户
"""
username = AdminConfig.USERNAME
name = AdminConfig.NAME
password = AdminConfig.PASSWORD
role = 2
email = AdminConfig.EMAIL
try:
# 采用aiomysql异步操作数据库
async with async_session() as session:
async with session.begin():
# 检查用户名和邮箱是否已存在
users = await session.execute(
select(User).where(or_(User.username == username, User.email == email)))
counts = await session.execute(select(func.count(User.id)))
if users.scalars().first():
logger.info("admin用户已存在")
return True
# 注册时给密码加盐
pwd = AbandonJWT.add_salt(password)
user = User(username, name, pwd, email, role)
user.last_login_at = datetime.now()
session.add(user)
await session.flush()
session.expunge(user)
return True
except Exception as e:
logger.error(f"初始化admin用户失败: {str(e)}")
raise Exception(f"初始化admin用户失败: {e}")
Base.metadata.create_all(engine)
async def main():
await init_user()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
前端编写route限制
根据umi官网,我们可以熟悉:在umi中,搭配 access 官方文档一起使用,可以完成对路由权限的控制。因官方文档写的很明白,所以在此不多赘述,只是简单分成几步:
- 配置开启。同时需要
src/access.ts提供权限配置。 - 约定了
src/access.ts为我们的权限定义文件 - 扩展路由配置
新增src/access.ts文件
字面意思
约定权限定义文件
export default function access(initialState: { currentUser?: API.CurrentUser | undefined }) {
const { currentUser } = initialState;
return {
// 判断用户是否为超级管理员
isAdmin: currentUser && currentUser.usr_info.role === 2,
// // 判断用户是否为组长
// isLeader: currentUser && currentUser.usr_info.role === 1,
// 判断用户是否为普通用户
isUser: currentUser && currentUser.usr_info.role !== 2,
};
}
扩展路由配置
在路由中,如图添加对应字段,即可限制路由权限。如果不加,默认是不需要权限。需要哪个权限,则添加对应字段即可。在此我们直接看最终效果:
超级管理员的菜单:
普通用户和组长的菜单:
至此,我们就实现了前后端根据不同用户权限展示不同菜单的功能,但是我在开头有说过,我们在写这部分功能的时候遇到了一个BUG,下面是我的BUG描述。
BUG描述
【简述】:首次登陆时,展示的菜单内容与实际不符,但是刷新页面后又正常展示。 【截图】:
【BUG定位】:我的login方法可以看到的我里面用的是异步请求。但是这里我遇到了问题:在我渲染页面的时候,我的返回还没有到,所以每次鉴权代码拿到的数据都是空,因此鉴权出来的没有匹配上isAdmin和isUser。导致最终展示出来的菜单,是没有限制任何权限的菜单。
【打印信息第二次定位】:
我们在鉴权代码中,加上打印信息,然后查看数据。
清除本地存储后的首次登陆打印:
刷新页面的第二次打印:
【解决方案】:暂时把这个BUG留给评论区的各位,大家可以提出自己的修改建议,一起成长!下一篇文章会将解决方案放出。
开源相关
加群一起讨论相关问题呀!如果群二维码过期了,可以加我个人微信: yyi11yy 我拉你进群~