27 KiB
统一登录网关平台
一、项目概述
1.1 项目背景
当前存在多个独立业务系统,各系统均有独立的登录模块、用户体系和权限管理,导致用户需要记忆多套账号密码,管理员需要在多个系统中重复维护用户信息,权限管理混乱且难以统一管控。
1.2 项目目标
构建一个统一的多系统登录与权限网关平台,实现:
- 统一登录入口:所有业务系统共用一个登录页面(https://login.user.mokee.com),用户一次登录即可访问所有授权系统
- 统一用户管理:所有用户信息集中在网关平台维护,业务系统不再存储用户数据
- 统一权限管理:所有业务系统的菜单、角色、权限集中在网关平台配置
- 统一接口转发:所有业务系统的接口请求统一经过网关转发,网关负责身份认证和用户信息透传
- 可配置化接入:新增业务系统无需修改网关代码,仅需在后台配置系统信息即可快速接入
- 智能系统跳转:登录成功后自动判断用户绑定系统数量,多系统展示选择页,单系统直接跳转
1.3 核心价值
- 提升用户体验:用户无需记忆多套账号密码,一次登录即可访问所有授权系统
- 降低管理成本:管理员只需在一个平台维护用户和权限信息
- 提高系统安全性:统一身份认证和接口鉴权,避免非法访问
- 便于系统扩展:新增业务系统可快速接入,无需重复开发登录和权限模块
二、技术架构
2.1 整体架构图
[用户浏览器]
↓
[业务系统前端] → [统一登录页面(https://login.user.zgitm.com)]
↓
[网关服务(9000)] → [认证用户服务(9001)]
↓ ↓
[业务系统后端] ← [业务管理服务(9002)]
↓
[MySQL数据库] ← [Redis缓存]
2.2 微服务拆分与职责
2.2.1 网关服务(mokee-gateway-gateway)- 端口9000
- 统一入口:所有业务系统的接口请求和页面访问统一经过网关
- 路由转发:根据配置的系统信息,将请求转发到对应的业务系统后端
- 身份认证:校验请求中的Token有效性,拦截未登录请求
- 信息透传:将用户ID、用户名、角色、岗位、系统ID等信息注入请求头,传递给业务系统后端
- 接口鉴权:校验请求接口是否在业务系统的放行列表中
2.2.2 认证用户服务(mokee-gateway-auth)- 端口9001
- 登录认证:处理用户登录请求,验证账号密码,生成Token
- 用户管理:维护用户基本信息、用户与系统的绑定关系
- 权限管理:维护角色、菜单信息,用户与角色的绑定关系
- 会话管理:管理Redis中的用户登录会话,处理退出登录请求
- 基础接口:提供获取用户信息、系统信息、菜单路由等基础接口
- 系统选择:登录成功后判断用户绑定系统数量,返回系统列表或直接跳转
2.2.3 业务管理服务(mokee-gateway-admin)- 端口9002
- 系统管理:维护业务系统的基本信息、域名配置、接口放行列表
- 数据字典:维护全局通用的数据字典
- 日志管理:记录用户登录日志和系统操作日志
- 定时任务:执行系统定时任务,如自动锁定长期未登录用户
2.3 技术栈明细
| 技术领域 | 技术选型 | 版本要求 |
|---|---|---|
| 后端框架 | Spring Boot | 3.0+ |
| 开发语言 | Java | 17.0.12+ |
| 前端框架 | Vue3 | 最新稳定版 |
| 数据库 | MySQL | 8.0+ |
| 缓存 | Redis | 最新稳定版 |
| 服务通信 | OpenFeign | Spring Boot 3配套版本 |
| 日志 | SLF4J + Logback | Spring Boot 3配套版本 |
| 部署 | Kubernetes | 用户自行部署 |
2.4 环境配置
2.4.1 数据库配置
spring:
datasource:
url: jdbc:mysql://db1.prod.zgitm.com:3306/mokeegateway?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: mokeegateway
password: mokee.
driver-class-name: com.mysql.cj.jdbc.Driver
2.4.2 Redis配置
spring:
redis:
host: redis.db1.zgitm.com
port: 6379
database: 1
password: mokee2016.
timeout: 3000ms
2.4.3 开发环境配置
- JDK路径:E:\javaJdk\jdk-17.0.12
- Maven路径:E:\apache-maven-3.6.0
- 统一登录平台本地开发地址:http://127.0.0.1:3300
- 统一登录平台生产域名:https://login.user.zgitm.com
三、核心功能实现
3.1 统一登录功能(新增系统选择页)
3.1.1 登录流程(修订版)
- 用户访问任意业务系统前端域名
- 业务系统前端检测本地无有效Token,自动跳转至统一登录页面:https://login.user.mokee.com
- 跳转时携带回调参数:redirect=当前业务系统完整地址(可选,若用户直接访问登录页则无此参数)
- 用户在统一登录页面输入账号/邮箱+密码
- 认证用户服务验证账号密码,验证通过后生成Token
- 将Token和用户信息存入Redis,设置过期时间
- 系统选择逻辑:
- 查询当前用户绑定的所有启用状态的业务系统
- 若用户只绑定了1个系统:自动跳转至该系统的前端地址
- 若用户绑定了多个系统:跳转至网关平台的系统选择页面,展示所有可访问系统列表
- 用户点击选择目标系统后,跳转至对应系统的前端地址
- 业务系统前端将Token存入本地存储
3.1.2 系统选择页面功能
- 展示用户所有可访问的业务系统卡片(包含系统名称、系统图标、系统描述)
- 点击系统卡片自动跳转至对应系统的前端地址
- 支持退出登录功能,跳转回统一登录页
- 页面适配PC端和移动端
3.1.3 登录成功返回信息
{
"code": 200,
"msg": "登录成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"userInfo": {
"userId": "123e4567-e89b-12d3-a456-426614174000",
"username": "张三",
"email": "zhangsan@example.com",
"phone": "13800138000",
"avatar": "https://example.com/avatar.jpg",
"post": "产品经理"
},
"systemList": [
{
"systemId": "456e4567-e89b-12d3-a456-426614174001",
"systemCode": "msg",
"systemName": "信息管理系统",
"frontUrl": "https://web.msg.mokee.com"
},
{
"systemId": "789e4567-e89b-12d3-a456-426614174002",
"systemCode": "oa",
"systemName": "办公自动化系统",
"frontUrl": "https://web.oa.mokee.com"
}
],
"needSelectSystem": true
},
"timestamp": 1718000000000
}
3.2 系统配置功能
3.2.1 系统信息配置
每个业务系统需要配置以下信息:
- 系统编码:唯一标识业务系统的字符串(如:msg、oa、finance)
- 系统名称:业务系统的中文名称(如:信息管理系统、办公自动化系统)
- 系统前端地址:业务系统前端的访问域名(如:https://web.msg.mokee.com)
- 系统后端地址:业务系统后端的真实服务地址(如:https://server.msg.mokee.com)
- 网关API域名:业务系统专用的 API 域名,DNS 指向网关服务器(如:https://or.test.mokee.com),网关通过域名自动识别业务系统
- 系统图标:系统选择页面展示的图标
- 系统状态:启用/禁用
- 系统描述:业务系统的简要说明
3.2.2 接口放行配置
每个业务系统需要配置允许访问的接口列表:
- 接口路径:接口的请求路径(如:/zgapi/v1/user/list)
- 请求方式:GET/POST/PUT/DELETE
- 接口名称:接口的中文名称
- 接口状态:启用/禁用
3.3 接口转发功能
3.3.1 转发流程
- 业务系统前端使用独立的 API 域名(DNS 指向网关),携带 Token 发起接口请求
- 网关服务从请求头中获取Token,校验Token有效性
- 从Redis中获取用户信息和当前系统信息
- 根据请求的 Host 头域名,匹配对应业务系统的 gateway_url,自动识别业务系统
- 校验请求接口是否在该业务系统的放行列表中
- 注入请求头信息:将用户ID、用户名、角色、岗位、系统ID等信息注入请求头
- 将请求转发到业务系统的真实后端地址
- 将业务系统后端的响应返回给前端
3.3.2 请求头注入信息(完整)
网关服务在转发请求时,会在请求头中注入以下信息,业务系统后端直接从请求头获取即可:
| 请求头字段 | 说明 |
|---|---|
| X-User-Id | 当前登录用户的唯一ID(UUID) |
| X-User-Name | 当前登录用户的真实姓名 |
| X-User-Account | 当前登录用户的登录账号 |
| X-User-Email | 当前登录用户的邮箱 |
| X-User-Post | 当前登录用户的岗位 |
| X-User-Roles | 当前用户在该系统下的角色列表(JSON数组字符串) |
| X-System-Id | 当前访问的业务系统ID |
| X-System-Code | 当前访问的业务系统编码 |
| X-System-Name | 当前访问的业务系统名称 |
3.3.3 业务系统后端处理
业务系统后端无需实现任何登录和权限逻辑,只需从请求头中获取上述用户和系统信息,进行业务处理和数据隔离即可。
3.4 菜单与权限管理
3.4.1 菜单配置
每个业务系统的菜单在网关平台配置,包括:
- 菜单名称
- 菜单路径
- 前端组件路径
- 菜单图标
- 菜单类型(目录/菜单/按钮)
- 排序
- 状态
3.4.2 角色管理
每个业务系统可以创建多个角色,角色与菜单绑定:
- 角色名称
- 角色编码
- 角色描述
- 绑定的菜单列表
3.4.3 用户授权
用户与业务系统绑定,在每个业务系统下可以分配一个或多个角色:
- 用户绑定业务系统
- 在业务系统下为用户分配角色
- 用户的权限为所分配角色的权限合并
3.4.4 动态路由生成
业务系统前端登录成功后,向网关请求当前用户在该系统下的菜单路由:
- 前端携带Token请求菜单接口(网关通过域名自动识别系统)
- 认证用户服务查询用户在该系统下的所有角色
- 合并所有角色的菜单权限
- 生成Vue动态路由JSON返回给前端
- 前端根据路由JSON动态生成路由并渲染页面
3.5 日志管理
3.5.1 登录日志
记录所有用户的登录信息,包括:
- 用户ID
- 用户名
- 登录时间
- 登录IP
- 登录系统
- 登录状态(成功/失败)
- 浏览器信息
- 操作系统信息
3.5.2 操作日志
通过AOP切面自动记录所有用户的操作信息,包括:
- 操作人ID
- 操作人名称
- 操作时间
- 操作模块
- 操作类型(新增/编辑/删除/查询)
- 请求地址
- 请求方式
- 请求参数
- 响应结果
- 操作状态(成功/失败)
3.6 定时任务
系统内置定时任务:
- 自动锁定长期未登录用户:每日扫描用户登录日志,自动锁定连续1年未登录的用户账号
- 清理过期日志:定期清理超过保留期限的登录日志和操作日志
四、数据库设计
4.1 核心数据表
4.1.1 系统表(sys_system)
| 字段名 | 数据类型 | 主键 | 说明 |
|---|---|---|---|
| system_id | varchar(36) | 是 | 系统ID(UUID) |
| system_code | varchar(64) | 否 | 系统编码(唯一) |
| system_name | varchar(128) | 否 | 系统名称 |
| front_url | varchar(256) | 否 | 系统前端地址 |
| backend_url | varchar(256) | 否 | 系统后端真实地址 |
| gateway_url | varchar(256) | 否 | 网关代理地址(唯一) |
| icon | varchar(256) | 否 | 系统图标地址 |
| status | tinyint | 否 | 系统状态(0-禁用,1-启用) |
| description | text | 否 | 系统描述 |
| create_time | datetime | 否 | 创建时间 |
| update_time | datetime | 否 | 更新时间 |
4.1.2 系统接口表(sys_system_api)
| 字段名 | 数据类型 | 主键 | 说明 |
|---|---|---|---|
| api_id | varchar(36) | 是 | 接口ID(UUID) |
| system_id | varchar(36) | 否 | 关联系统ID |
| api_path | varchar(256) | 否 | 接口路径 |
| api_method | varchar(16) | 否 | 请求方式 |
| api_name | varchar(128) | 否 | 接口名称 |
| status | tinyint | 否 | 接口状态(0-禁用,1-启用) |
| create_time | datetime | 否 | 创建时间 |
| update_time | datetime | 否 | 更新时间 |
4.1.3 用户表(sys_user)
| 字段名 | 数据类型 | 主键 | 说明 |
|---|---|---|---|
| user_id | varchar(36) | 是 | 用户ID(UUID) |
| username | varchar(64) | 否 | 登录账号(唯一) |
| password | varchar(128) | 否 | 加密密码 |
| varchar(64) | 否 | 邮箱(唯一) | |
| phone | varchar(32) | 否 | 手机号 |
| real_name | varchar(64) | 否 | 真实姓名 |
| avatar | varchar(256) | 否 | 头像地址 |
| post | varchar(64) | 否 | 岗位 |
| status | tinyint | 否 | 用户状态(0-禁用,1-正常) |
| last_login_time | datetime | 否 | 最后登录时间 |
| create_time | datetime | 否 | 创建时间 |
| update_time | datetime | 否 | 更新时间 |
4.1.4 用户系统关联表(sys_user_system)
| 字段名 | 数据类型 | 主键 | 说明 |
|---|---|---|---|
| id | varchar(36) | 是 | 关联ID(UUID) |
| user_id | varchar(36) | 否 | 用户ID |
| system_id | varchar(36) | 否 | 系统ID |
| create_time | datetime | 否 | 绑定时间 |
4.1.5 角色表(sys_role)
| 字段名 | 数据类型 | 主键 | 说明 |
|---|---|---|---|
| role_id | varchar(36) | 是 | 角色ID(UUID) |
| system_id | varchar(36) | 否 | 关联系统ID |
| role_name | varchar(64) | 否 | 角色名称 |
| role_code | varchar(64) | 否 | 角色编码 |
| description | text | 否 | 角色描述 |
| status | tinyint | 否 | 角色状态(0-禁用,1-启用) |
| create_time | datetime | 否 | 创建时间 |
| update_time | datetime | 否 | 更新时间 |
4.1.6 用户角色关联表(sys_user_role)
| 字段名 | 数据类型 | 主键 | 说明 |
|---|---|---|---|
| id | varchar(36) | 是 | 关联ID(UUID) |
| user_id | varchar(36) | 否 | 用户ID |
| system_id | varchar(36) | 否 | 系统ID |
| role_id | varchar(36) | 否 | 角色ID |
| create_time | datetime | 否 | 绑定时间 |
4.1.7 菜单表(sys_menu)
| 字段名 | 数据类型 | 主键 | 说明 |
|---|---|---|---|
| menu_id | varchar(36) | 是 | 菜单ID(UUID) |
| system_id | varchar(36) | 否 | 关联系统ID |
| parent_id | varchar(36) | 否 | 父菜单ID |
| menu_name | varchar(64) | 否 | 菜单名称 |
| menu_path | varchar(256) | 否 | 菜单路径 |
| component | varchar(256) | 否 | 前端组件路径 |
| icon | varchar(128) | 否 | 菜单图标 |
| menu_type | tinyint | 否 | 菜单类型(1-目录,2-菜单,3-按钮) |
| permission | varchar(128) | 否 | 权限标识 |
| sort | int | 否 | 排序 |
| status | tinyint | 否 | 菜单状态(0-禁用,1-启用) |
| create_time | datetime | 否 | 创建时间 |
| update_time | datetime | 否 | 更新时间 |
4.1.8 角色菜单关联表(sys_role_menu)
| 字段名 | 数据类型 | 主键 | 说明 |
|---|---|---|---|
| id | varchar(36) | 是 | 关联ID(UUID) |
| role_id | varchar(36) | 否 | 角色ID |
| menu_id | varchar(36) | 否 | 菜单ID |
4.1.9 登录日志表(sys_login_log)
| 字段名 | 数据类型 | 主键 | 说明 |
|---|---|---|---|
| log_id | varchar(36) | 是 | 日志ID(UUID) |
| user_id | varchar(36) | 否 | 用户ID |
| username | varchar(64) | 否 | 登录账号 |
| system_id | varchar(36) | 否 | 登录系统ID |
| login_ip | varchar(64) | 否 | 登录IP |
| login_time | datetime | 否 | 登录时间 |
| status | tinyint | 否 | 登录状态(0-失败,1-成功) |
| browser | varchar(64) | 否 | 浏览器信息 |
| os | varchar(64) | 否 | 操作系统信息 |
4.1.10 操作日志表(sys_operation_log)
| 字段名 | 数据类型 | 主键 | 说明 |
|---|---|---|---|
| log_id | varchar(36) | 是 | 日志ID(UUID) |
| user_id | varchar(36) | 否 | 操作人ID |
| username | varchar(64) | 否 | 操作人名称 |
| system_id | varchar(36) | 否 | 操作系统ID |
| module | varchar(64) | 否 | 操作模块 |
| operation | varchar(32) | 否 | 操作类型 |
| request_url | varchar(256) | 否 | 请求地址 |
| request_method | varchar(16) | 否 | 请求方式 |
| request_param | text | 否 | 请求参数 |
| response_result | text | 否 | 响应结果 |
| status | tinyint | 否 | 操作状态(0-失败,1-成功) |
| operation_time | datetime | 否 | 操作时间 |
五、接口规范
5.1 统一返回格式
所有接口统一返回以下格式:
{
"code": 200,
"msg": "操作成功",
"data": {},
"timestamp": 1718000000000
}
- code:响应状态码,200表示成功,其他表示失败
- msg:响应消息
- data:响应数据
- timestamp:响应时间戳
5.2 主要接口列表
5.2.1 认证接口
| 接口地址 | 请求方式 | 接口描述 |
|---|---|---|
| /zgapi/v1/auth/login | POST | 用户登录 |
| /zgapi/v1/auth/logout | POST | 退出登录 |
| /zgapi/v1/auth/user/info | GET | 获取当前用户信息 |
| /zgapi/v1/auth/menu/router | GET | 获取当前用户菜单路由 |
| /zgapi/v1/auth/password/update | PUT | 修改密码 |
| /zgapi/v1/auth/system/list | GET | 获取当前用户可访问系统列表 |
六、外部系统对接网关平台申请说明文档
注:在系统管理列表中,新增文档列,点击可以查看系统对接文档的超链接,这个是不要认证直接访问的。
6.1 对接前提
- 已在网关平台后台完成业务系统信息配置(系统编码、前端地址、后端地址、网关API域名)
- 业务系统的 API 域名 DNS 已指向网关服务器,网关通过域名自动识别业务系统
- 已在网关平台后台配置业务系统需要放行的接口列表
- 已在网关平台后台为用户分配该业务系统的访问权限和角色
6.2 前端系统修改步骤
6.2.1 移除原有登录功能
- 删除业务系统原有的登录页面、登录接口、登录逻辑
- 删除业务系统原有的本地用户存储、Token管理逻辑
6.2.2 配置全局路由守卫(核心)
在业务系统的router/index.ts中添加全局路由守卫,实现未登录自动跳转统一登录页:
import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from '@/stores/user'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
// 业务系统原有路由(无需保留登录路由)
]
})
// 全局路由守卫
router.beforeEach((to, from, next) => {
const userStore = useUserStore()
const token = localStorage.getItem('token')
// 统一登录平台地址
const LOGIN_URL = 'https://login.mokeeuserlogin.com'
// 当前业务系统编码(从网关平台获取,用于登录回调参数)
const SYSTEM_CODE = 'msg'
if (!token) {
// 未登录,跳转到统一登录页,携带当前页面地址作为回调
window.location.href = `${LOGIN_URL}?redirect=${encodeURIComponent(window.location.href)}&systemCode=${SYSTEM_CODE}`
return
}
// 已登录,继续访问
next()
})
export default router
6.2.3 配置全局请求拦截器
在业务系统的utils/request.ts中配置全局请求拦截器,统一携带Token。
重要说明:业务系统使用独立的 API 域名(如
https://or.test.mokee.com),DNS 指向网关服务器。网关通过请求的 Host 头域名自动识别业务系统,前端无需再发送X-System-Code请求头。
import axios from 'axios'
const service = axios.create({
// 业务系统独立的 API 域名(DNS 指向网关,从网关平台获取)
baseURL: 'https://or.test.mokee.com',
timeout: 30000
})
// 请求拦截器
service.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => {
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
(response) => {
return response.data
},
(error) => {
// 401未授权,跳转到统一登录页
if (error.response && error.response.status === 401) {
localStorage.removeItem('token')
window.location.href = 'https://login.mokeeuserlogin.com'
}
return Promise.reject(error)
}
)
export default service
6.2.4 实现动态路由加载
在业务系统的main.ts中添加动态路由加载逻辑:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { useUserStore } from './stores/user'
const app = createApp(App)
// 初始化时加载动态路由
const initApp = async () => {
const userStore = useUserStore()
const token = localStorage.getItem('token')
if (token) {
try {
// 从网关获取当前用户在该系统下的菜单路由
const res = await userStore.getMenuRouter()
// 动态添加路由
res.data.forEach((route) => {
router.addRoute(route)
})
} catch (error) {
console.error('加载动态路由失败', error)
localStorage.removeItem('token')
window.location.href = 'https://login.mokeeuserlogin.com'
}
}
app.use(router)
app.mount('#app')
}
initApp()
6.2.5 获取用户信息
在业务系统的stores/user.ts中实现获取用户信息和菜单路由的方法:
import { defineStore } from 'pinia'
import request from '@/utils/request'
export const useUserStore = defineStore('user', {
state: () => ({
userInfo: null,
menuList: []
}),
actions: {
// 获取当前用户信息
async getUserInfo() {
const res = await request.get('/api/v1/auth/user/info')
this.userInfo = res.data
return res.data
},
// 获取当前用户菜单路由
async getMenuRouter() {
const res = await request.get('/api/v1/auth/menu/router', {
params: {
systemCode: 'msg'
}
})
this.menuList = res.data
return res.data
}
}
})
6.3 后端系统修改步骤
6.3.1 移除原有登录和权限逻辑
- 删除业务系统原有的登录接口、用户表、角色表、权限表
- 删除业务系统原有的Token校验、权限拦截逻辑
6.3.2 从请求头获取用户信息(核心)
业务系统后端无需访问网关,所有用户和系统信息都从网关注入的请求头中获取。以下是Java示例:
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1/user")
public class UserController {
@GetMapping("/current")
public UserInfo getCurrentUser(HttpServletRequest request) {
// 从请求头获取用户信息
String userId = request.getHeader("X-User-Id");
String userName = request.getHeader("X-User-Name");
String userAccount = request.getHeader("X-User-Account");
String userEmail = request.getHeader("X-User-Email");
String userPost = request.getHeader("X-User-Post");
String userRoles = request.getHeader("X-User-Roles");
String systemId = request.getHeader("X-System-Id");
String systemCode = request.getHeader("X-System-Code");
// 构建用户信息对象
UserInfo userInfo = new UserInfo();
userInfo.setUserId(userId);
userInfo.setUserName(userName);
userInfo.setUserAccount(userAccount);
userInfo.setUserEmail(userEmail);
userInfo.setUserPost(userPost);
// 解析角色列表
userInfo.setRoles(JSON.parseArray(userRoles, String.class));
userInfo.setSystemId(systemId);
userInfo.setSystemCode(systemCode);
return userInfo;
}
}
6.3.3 实现数据隔离
根据请求头中的用户ID和系统ID,实现业务数据的隔离:
@GetMapping("/list")
public List<BusinessData> getBusinessData(HttpServletRequest request) {
String userId = request.getHeader("X-User-Id");
String systemId = request.getHeader("X-System-Id");
// 根据用户ID和系统ID查询该用户在该系统下的业务数据
return businessDataService.listByUserIdAndSystemId(userId, systemId);
}
6.4 对接测试步骤
- 启动网关平台和业务系统
- 访问业务系统前端地址,验证是否自动跳转到统一登录页
- 使用测试账号登录,验证是否正确跳转到系统选择页或直接进入业务系统
- 验证业务系统是否能正确获取用户信息和菜单路由
- 验证业务系统接口请求是否经过网关转发,后端是否能正确获取请求头中的用户信息
- 验证未授权接口是否被网关拦截
- 验证退出登录功能是否正常
6.5 常见问题
- 跨域问题:请在网关服务中配置跨域支持,允许业务系统前端域名访问
- Token过期:请在前端响应拦截器中处理401状态码,自动跳转到统一登录页
- 请求头丢失:请确保网关服务在转发请求时正确保留和注入请求头
- 动态路由加载失败:请检查网关平台是否正确配置了该系统的菜单和用户权限
附录:文档审查问题清单
🔴 严重不一致(必须修正)
#1 统一登录域名不一致
- 第 3.1.1 节:
https://login.user.mokee.com - 第 6.2.2 节及后续多处:
https://login.mokeeuserlogin.com - 建议统一为:
https://login.user.mokee.com
#2 API 路径前缀不一致
- 第 5.2.1 节接口列表使用:
/zgapi/v1/auth/... - 第 6.2.5 节前端示例代码使用:
/api/v1/auth/...(缺少zg前缀) - 建议统一为:
/zgapi/v1/...
#3 登录返回数据中的待确认项
- 第 3.1.3 节返回 JSON 注释:
//还要不要加一个当前选着系统?("选着"是错别字,应为"选择") - 需确认:是否需要返回"当前选中系统"信息?
🟡 数据表设计问题(建议修正)
#4 sys_user_role 表冗余字段
sys_user_role表有system_id字段,但role_id关联的sys_role表已包含system_id—— 不一致风险
#5 缺少逻辑删除字段
- 所有表无
is_deleted字段
#6 缺少审计字段
- 所有表缺少
created_by/updated_by
#7 sys_role_menu 表缺少时间字段
- 与其他关联表(
sys_user_system、sys_user_role)不一致
#8 sys_user 表缺少字段
- 缺少
last_login_ip - 密码加密算法未明确(BCrypt?SCrypt?)
#9 未定义索引策略
- 所有表未提及索引设计
🟠 功能缺失(建议补充)
#10 缺少 Token 刷新机制
#11 缺少统一登出(SSO Logout)
#12 缺少登录安全措施
- 无验证码机制
- 无密码复杂度策略
- 无账号锁定策略(多次失败锁定)
- 无限流机制
#13 缺少跨域(CORS)配置方案
#14 X-User-Roles 请求头格式未明确
- 数组元素包含哪些字段?
🔵 细节问题
#15 needSelectSystem 字段冗余
- 前端可通过
systemList.length > 1判断
#16 前端代码示例问题
- 路由守卫缺
async - 每次路由切换都读 localStorage(性能)
#17 第 3 节与第 6 节内容重叠