Files
mokeegateway/统一登录网关平台-需求文档.md
2026-07-16 00:04:27 +08:00

27 KiB
Raw Blame History

统一登录网关平台

一、项目概述

1.1 项目背景

当前存在多个独立业务系统,各系统均有独立的登录模块、用户体系和权限管理,导致用户需要记忆多套账号密码,管理员需要在多个系统中重复维护用户信息,权限管理混乱且难以统一管控。

1.2 项目目标

构建一个统一的多系统登录与权限网关平台,实现:

  • 统一登录入口:所有业务系统共用一个登录页面(https://login.user.mokee.com用户一次登录即可访问所有授权系统
  • 统一用户管理:所有用户信息集中在网关平台维护,业务系统不再存储用户数据
  • 统一权限管理:所有业务系统的菜单、角色、权限集中在网关平台配置
  • 统一接口转发:所有业务系统的接口请求统一经过网关转发,网关负责身份认证和用户信息透传
  • 可配置化接入:新增业务系统无需修改网关代码,仅需在后台配置系统信息即可快速接入
  • 智能系统跳转:登录成功后自动判断用户绑定系统数量,多系统展示选择页,单系统直接跳转

1.3 核心价值

  • 提升用户体验:用户无需记忆多套账号密码,一次登录即可访问所有授权系统
  • 降低管理成本:管理员只需在一个平台维护用户和权限信息
  • 提高系统安全性:统一身份认证和接口鉴权,避免非法访问
  • 便于系统扩展:新增业务系统可快速接入,无需重复开发登录和权限模块

二、技术架构

2.1 整体架构图

[用户浏览器]
    ↓
[业务系统前端] → [统一登录页面(https://login.user.zgitm.com)]
    ↓
[网关服务(32000)] → [认证用户服务(32001)]
    ↓                ↓
[业务系统后端] ← [业务管理服务(32002)]
    ↓
[MySQL数据库] ← [Redis缓存]

2.2 微服务拆分与职责

2.2.1 网关服务mokee-gateway-gateway- 端口32000

  • 统一入口:所有业务系统的接口请求和页面访问统一经过网关
  • 路由转发:根据配置的系统信息,将请求转发到对应的业务系统后端
  • 身份认证校验请求中的Token有效性拦截未登录请求
  • 信息透传将用户ID、用户名、角色、岗位、系统ID等信息注入请求头传递给业务系统后端
  • 接口鉴权:校验请求接口是否在业务系统的放行列表中

2.2.2 认证用户服务mokee-gateway-auth- 端口32001

  • 登录认证处理用户登录请求验证账号密码生成Token
  • 用户管理:维护用户基本信息、用户与系统的绑定关系
  • 权限管理:维护角色、菜单信息,用户与角色的绑定关系
  • 会话管理管理Redis中的用户登录会话处理退出登录请求
  • 基础接口:提供获取用户信息、系统信息、菜单路由等基础接口
  • 系统选择:登录成功后判断用户绑定系统数量,返回系统列表或直接跳转

2.2.3 业务管理服务mokee-gateway-admin- 端口32002

  • 系统管理:维护业务系统的基本信息、域名配置、接口放行列表
  • 数据字典:维护全局通用的数据字典
  • 日志管理:记录用户登录日志和系统操作日志
  • 定时任务:执行系统定时任务,如自动锁定长期未登录用户

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 开发环境配置


三、核心功能实现

3.1 统一登录功能(新增系统选择页)

3.1.1 登录流程(修订版)

  1. 用户访问任意业务系统前端域名
  2. 业务系统前端检测本地无有效Token自动跳转至统一登录页面https://login.user.mokee.com
  3. 跳转时携带回调参数redirect=当前业务系统完整地址(可选,若用户直接访问登录页则无此参数)
  4. 用户在统一登录页面输入账号/邮箱+密码
  5. 认证用户服务验证账号密码验证通过后生成Token
  6. 将Token和用户信息存入Redis设置过期时间
  7. 系统选择逻辑:
    • 查询当前用户绑定的所有启用状态的业务系统
    • 若用户只绑定了1个系统自动跳转至该系统的前端地址
    • 若用户绑定了多个系统:跳转至网关平台的系统选择页面,展示所有可访问系统列表
    • 用户点击选择目标系统后,跳转至对应系统的前端地址
  8. 业务系统前端将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 系统信息配置

每个业务系统需要配置以下信息:

3.2.2 接口放行配置

每个业务系统需要配置允许访问的接口列表:

  • 接口路径:接口的请求路径(如:/zgapi/v1/user/list
  • 请求方式GET/POST/PUT/DELETE
  • 接口名称:接口的中文名称
  • 接口状态:启用/禁用

3.3 接口转发功能

3.3.1 转发流程

  1. 业务系统前端使用独立的 API 域名DNS 指向网关),携带 Token 发起接口请求
  2. 网关服务从请求头中获取Token校验Token有效性
  3. 从Redis中获取用户信息和当前系统信息
  4. 根据请求的 Host 头域名,匹配对应业务系统的 gateway_url自动识别业务系统
  5. 校验请求接口是否在该业务系统的放行列表中
  6. 注入请求头信息将用户ID、用户名、角色、岗位、系统ID等信息注入请求头
  7. 将请求转发到业务系统的真实后端地址
  8. 将业务系统后端的响应返回给前端

3.3.2 请求头注入信息(完整)

网关服务在转发请求时,会在请求头中注入以下信息,业务系统后端直接从请求头获取即可:

请求头字段 说明
X-User-Id 当前登录用户的唯一IDUUID
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 动态路由生成

业务系统前端登录成功后,向网关请求当前用户在该系统下的菜单路由:

  1. 前端携带Token请求菜单接口网关通过域名自动识别系统
  2. 认证用户服务查询用户在该系统下的所有角色
  3. 合并所有角色的菜单权限
  4. 生成Vue动态路由JSON返回给前端
  5. 前端根据路由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) 系统IDUUID
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) 接口IDUUID
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) 用户IDUUID
username varchar(64) 登录账号(唯一)
password varchar(128) 加密密码
email 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) 关联IDUUID
user_id varchar(36) 用户ID
system_id varchar(36) 系统ID
create_time datetime 绑定时间

4.1.5 角色表sys_role

字段名 数据类型 主键 说明
role_id varchar(36) 角色IDUUID
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) 关联IDUUID
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) 菜单IDUUID
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) 关联IDUUID
role_id varchar(36) 角色ID
menu_id varchar(36) 菜单ID

4.1.9 登录日志表sys_login_log

字段名 数据类型 主键 说明
log_id varchar(36) 日志IDUUID
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) 日志IDUUID
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 对接前提

  1. 已在网关平台后台完成业务系统信息配置系统编码、前端地址、后端地址、网关API域名
  2. 业务系统的 API 域名 DNS 已指向网关服务器,网关通过域名自动识别业务系统
  3. 已在网关平台后台配置业务系统需要放行的接口列表
  4. 已在网关平台后台为用户分配该业务系统的访问权限和角色

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.comDNS 指向网关服务器。网关通过请求的 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 对接测试步骤

  1. 启动网关平台和业务系统
  2. 访问业务系统前端地址,验证是否自动跳转到统一登录页
  3. 使用测试账号登录,验证是否正确跳转到系统选择页或直接进入业务系统
  4. 验证业务系统是否能正确获取用户信息和菜单路由
  5. 验证业务系统接口请求是否经过网关转发,后端是否能正确获取请求头中的用户信息
  6. 验证未授权接口是否被网关拦截
  7. 验证退出登录功能是否正常

6.5 常见问题

  1. 跨域问题:请在网关服务中配置跨域支持,允许业务系统前端域名访问
  2. Token过期请在前端响应拦截器中处理401状态码自动跳转到统一登录页
  3. 请求头丢失:请确保网关服务在转发请求时正确保留和注入请求头
  4. 动态路由加载失败:请检查网关平台是否正确配置了该系统的菜单和用户权限

附录:文档审查问题清单

🔴 严重不一致(必须修正)

#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_systemsys_user_role)不一致

#8 sys_user 表缺少字段

  • 缺少 last_login_ip
  • 密码加密算法未明确BCryptSCrypt

#9 未定义索引策略

  • 所有表未提及索引设计

🟠 功能缺失(建议补充)

#10 缺少 Token 刷新机制

#11 缺少统一登出SSO Logout

#12 缺少登录安全措施

  • 无验证码机制
  • 无密码复杂度策略
  • 无账号锁定策略(多次失败锁定)
  • 无限流机制

#13 缺少跨域CORS配置方案

#14 X-User-Roles 请求头格式未明确

  • 数组元素包含哪些字段?

🔵 细节问题

#15 needSelectSystem 字段冗余

  • 前端可通过 systemList.length > 1 判断

#16 前端代码示例问题

  • 路由守卫缺 async
  • 每次路由切换都读 localStorage性能

#17 第 3 节与第 6 节内容重叠