零信任平台接入芋道框架

1. 任务背景

由于要求,需要将我们自己基于yudao前后端开发的系统对接零信任平台。需要在尽可能不侵入我们自己的系统情况下完成零信任平台到我们系统的单点登录。

2. 零信任平台接入芋道框架流程图

3. 零信任平台接入芋道框架时序图

4. 代码改动

4.1. 前端核心改动( auth.ts、index.ts、user.ts、permission.ts**)**

前端的主要目标是:在应用加载时捕获外部令牌,使用该令牌向后端请求应用自身的Token,然后用应用Token完成登录并处理好多租户逻辑。

1. permission.ts**(路由守卫)**

改动一: 新增 handleSsoRedirect() 函数。

  • 原因: 浏览器中的JavaScript无法直接读取初始页面加载时的HTTP请求头。此函数通过读取URL参数 (?userToken=...) 的方式来捕获零信任令牌,并将其存入浏览器缓存,这是打通前后端的第一座桥梁。
  • 改动二: 修改 router.beforeEachelse 逻辑块。
  • 原因: 当应用内没有Token时,不再是立即跳转到登录页,而是进入一个 try...catch 流程,优先尝试执行SSO登录 (userStore.ssoLogin())。如果SSO成功,则正常进入系统;如果SSO失败(例如没有外部令牌),则回退到原来的逻辑,跳转到登录页。
2. store/modules/user.ts**(Pinia用户状态)**
  • 改动一: 新增 ssoLogin() action。
  • 原因: 这是执行SSO登录的核心业务逻辑。它负责调用API接口 (ssoLoginApi),并在成功后接收后端返回的应用Token和租户ID,然后调用工具函数将它们存入缓存。
  • 改动二: 修正 ssoLogin 方法中的数据处理。
  • 原因: 最初的 setToken(res.data) 是错误的,因为框架的axios拦截器已经解包了响应,res 本身就是数据体。修正为 setToken(res) 才不会导致 TypeError 错误。
  • 改動三:ssoLogin 方法中增加 setTenantId(res.tenantId) 调用。
  • 原因: 为了解决若依Pro框架的多租户权限问题。后端在SSO成功后会返回用户所属的 tenantId,前端必须将此ID存入缓存,以便后续所有API请求都能自动携带 tenant-id 请求头。
3. api/login.ts**(API接口定义)**
  • 改动: 新增 ssoLoginApi() 函数。
  • 原因: 定义一个专门用于发起SSO登录请求的API函数。它负责从缓存中读取 zeroTrustToken,并将其作为请求头,发送给后端专门处理SSO的过滤器接口 (/admin-api/system/auth/sso-login)。
4. utils/auth.ts**(认证工具函数)**
  • 改动一: 新增 getZeroTrustToken, setZeroTrustToken, removeZeroTrustToken 函数。
  • 原因: 提供一套标准的函数来管理从URL捕获到的、临时的零信任令牌对象,实现其在浏览器缓存中的存、取、删。
  • 改动二: 确保 setTenantId 函数被正确导出和导入。
  • 原因: 以便 user.ts 中的 ssoLogin action可以调用它来存储租户ID。

4.2. 后端核心改动

增加一个零信任模块,对其他模块不产生影响

后端的总体目标是:提供一个专门的接口(由Filter实现)来接收外部令牌,验证它,完成用户映射/创建,并返回一个应用自身的合法会话(Token)。

5. 零信任单点登录流程简述

5.1. 前端触发与令牌传递

  1. 用户发起访问 :用户通过包含 userTokenappToken参数的URL(如 http://your-app/?userToken=xxx&appToken=xxx)访问应用。
  2. Nginx代理与转发 :Nginx接收到请求,将Token注入请求头(如 RZZX-USERTOKEN),并代理至前端服务。
  3. 前端捕获Token:前端应用(如Vue)加载时,从URL参数中提取Token并存入本地(如LocalStorage)。
  4. 发起SSO登录请求:前端路由守卫检测到未登录状态,自动调用SSO登录接口,并将存储的Token通过请求头发送至后端。

5.2. 后端认证与用户处理

  1. 过滤器拦截与验证 :后端定义一个专门的认证过滤器(如 ZeroTrustAuthenticationFilter),拦截SSO登录请求(如 /admin-api/system/auth/sso-login),提取请求头中的Token。
  2. 与零信任平台交互 :认证服务(如 ZeroTrustAuthService)调用零信任平台的接口(通常是基于HTTP的REST API)来验证Token的有效性并获取用户唯一标识(如 idcardpid)。
  3. 本地用户映射与创建
    1. 系统使用从零信任平台获取的用户标识,查询本地用户库。
    2. 如果用户存在,则更新其最新信息。
    3. 如果用户不存在 ,则根据业务规则在本地创建一个新用户账户,并为其分配一个默认的租户ID(如 tenantId=1**)**,这个过程确保了新用户能够立即在正确的租户上下文中工作。
  1. 创建本地会话 :用户映射成功后,调用框架的令牌服务(如 oauth2TokenApi.createAccessToken)为该用户创建本地会话(生成应用的Access Token和Refresh Token)。

5.3. 登录完成与后续访问

  1. 返回前端关键信息 :后端将生成的本地Token、租户ID(tenantId)等信息返回给前端。
  2. 前端建立登录状态:前端接收到响应后,将本地Token和租户ID等信息存储起来,并更新应用状态为已登录,随后跳转到系统主页面。
  3. 正常访问与租户隔离 :此后用户访问业务接口时,前端会在请求头(如 Authorization: Bearer <access_token>tenant-id: <tenantId>)中自动附加Token和租户ID。后端的租户过滤器(如 TenantSecurityWebFilter)会据此识别用户身份并确保其只能访问所属租户的数据,实现数据隔离。

5.4. 统一单点登出

  1. 用户发起登出:用户在前端点击登出。
  2. 通知零信任平台 :前端调用登出接口,后端不仅清除本地会话(如从Redis删除Token),还会调用零信任平台的令牌失效接口,使零信任侧的Token失效。
  3. 前端清理与重定向:前端清除本地存储的所有Token和状态,并可能重定向至零信任平台的登录页面,完成全局登出。

6. 零信任单点登录完整流程详解

整个流程可以分为七个主要阶段:

  1. 发起跳转:用户从外部系统被重定向到您的应用。
  2. Nginx 代理:Nginx作为网关,处理初始请求。
  3. 前端捕获与SSO触发:Vue应用加载,捕获令牌并发起SSO请求。
  4. 后端认证:核心认证逻辑,与零信任平台交互并创建本地会话。
  5. 前端会话建立:前端存储本地Token,完成登录。
  6. 登录后正常访问:用户以登录状态访问系统其他接口。
  7. 统一登出:用户登出,同时通知零信任平台。

6.1.1. 阶段一:发起跳转
  • 动作: 外部系统(或我们的测试HTML页面)将用户重定向到您的应用网关地址,并在URL参数中携带零信任平台的用户令牌和应用令牌。
  • 需求 : 对应您的需求文档 第1条
  • 示例:
    • 用户浏览器访问:http://localhost:8000/?userToken=test-user-token-123456&appToken=test-app-token-abcdef
    • localhost:8000 是您的Nginx代理地址。

6.1.2. 阶段二:Nginx 代理
  • 动作 : Nginx监听到 8000 端口的请求,根据您的 nginx.conf 配置进行处理。

  • 代码依据 : nginx.conf

  • Nginx

    map arg_userToken user_token_header {
    default arg_userToken; } map arg_appToken app_token_header { default arg_appToken;
    }

    server {
    listen 8000;
    # ...location / {
    proxy_pass http://127.0.0.1:83; # 代理到前端proxy_set_header RZZX-USERTOKEN user_token_header; proxy_set_header RZZX-APPTOKEN app_token_header;
    # ...
    }
    location /admin-api {
    proxy_pass http://127.0.0.1:48083; # 代理到后端
    }
    }

  • 流程:

    • Nginx的 map 指令读取URL中的 userTokenappToken 参数。
    • proxy_pass 将请求转发给运行在 83 端口的前端Vue应用。
    • proxy_set_header在这次请求中 ,将URL参数转换为了HTTP请求头 RZZX-USERTOKENRZZX-APPTOKEN(关键:这一步只是为了"传递"信息,前端 JS 本身无法直接读取请求头)

6.1.3. 阶段三:前端捕获与SSO触发
  • 动作: 浏览器加载前端应用,Vue Router开始工作。
  • 需求 : 对应您的需求文档 第2条 (通过URL参数间接实现)。
  • 代码依据 : src/permission.ts
  • 流程:
    • handleSsoRedirect() 函数首先执行,它通过 new URLSearchParams(window.location.search) 从当前浏览器URL中捕获 userTokenappToken
    • 调用 setZeroTrustToken({ userToken, appToken }) (位于src/utils/auth.ts),将这两个令牌存入浏览器的 localStorage 中,以备后续使用。
    • router.beforeEach 路由守卫启动,getAccessToken() 返回 false (因为此时还没有应用自身的Token)。
    • 代码进入 try...catch 块,执行 await userStore.ssoLogin()
    • userStore.ssoLogin() (位于 src/store/modules/user.ts) 调用 ssoLoginApi() (位于 src/api/login.ts)。
    • ssoLoginApi 函数从 localStorage 中读出刚刚存入的 zeroTrustTokens,将它们作为请求头,向后端 /admin-api/system/auth/sso-login 发起一个真正的SSO登录 POST 请求。

6.1.4. 阶段四:后端认证
  • 动作: 后端接收到SSO登录请求,执行完整的认证、用户映射、会话创建流程。
  • 需求 : 对应您的需求文档 第3、4、5、6条
  • 代码依据 : ZeroTrustAuthenticationFilter.java, ZeroTrustAuthServiceImpl.java, ZeroTrustServiceImpl.java
  • 流程:
    • 过滤器拦截 : ZeroTrustAuthenticationFilter 拦截到 /admin-api/system/auth/sso-login 请求,并从中提取 RZZX-USERTOKENRZZX-APPTOKEN 请求头。
    • 服务调用 : 过滤器调用 zeroTrustAuthService.authenticate(...)
    • 与零信任平台交互 (ZeroTrustAuthServiceImpl -> ZeroTrustServiceImpl):
      1. 验证令牌 : 发起 POST /.../user/token/verify 请求到Mockoon,验证令牌有效性。
      2. 获取PID : 发起 POST /.../user/token/get 请求到Mockoon,获取用户pid
      3. 获取用户信息 : 发起 POST /.../user/query 请求到Mockoon,使用pid获取用户的idcard等信息。
    • 用户映射/创建 (ZeroTrustAuthServiceImpl):
      1. 使用 idcard 调用 userMapper.selectByUsername(idcard) 在本地 system_users 表中查找用户。
      2. 如果 localUser == null,则调用 createUser 方法,创建一个新用户,并设置默认的角色、部门以及租户ID ( newUser.setTenantId(1L)****)
      3. 如果用户已存在,则直接使用该用户信息。
    • 生成应用Token : authenticate 方法返回 AdminUserDO 对象给过滤器。
    • 返回响应 : ZeroTrustAuthenticationFilter 调用 oauth2TokenApi.createAccessToken(...) 为该用户生成一个若依框架自身的 accessTokenrefreshToken。然后,它构造一个包含 accessToken, refreshToken, expiresTime, zeroTrustToken, 和 tenantIdMap,并将其作为成功的JSON响应返回给前端。

6.1.5. 阶段五:前端会话建立
  • 动作: 前端接收到后端成功的响应,存储应用Token,完成登录状态的建立。
  • 代码依据 : src/store/modules/user.ts, src/utils/auth.ts
  • 流程:
    • ssoLoginApi()Promise 成功 resolve,ssoLogin action 中的 res 变量获得了后端返回的包含Token和租户ID的对象。
    • setToken(res) 被调用,将 accessTokenrefreshToken 存入 localStorage
    • setTenantId(res.tenantId) 被调用,将 tenantId 存入 localStorage
    • userStore.ssoLogin() 执行成功,permission.ts 中的 await 结束。
    • 路由守卫调用 next({ ...to, replace: true }),再次进入导航流程。

6.1.6. 阶段六:登录后正常访问
  • 动作: 用户以已登录状态,访问系统内的其他页面和接口。
  • 流程:
    • 路由守卫 permission.ts 再次执行。
    • 这一次,getAccessToken()返回 true
    • 代码进入 if(getAccessToken()) 的逻辑,开始加载用户信息、动态路由等,最终成功渲染出系统主页。
    • 当用户操作页面,触发新的API请求(如获取字典数据)时,前端的 axios 请求拦截器会自动从 localStorage 中读取 accessTokentenantId,并将它们添加到请求头中。
    • 后端的 TokenAuthenticationFilterTenantSecurityWebFilter 会验证这些请求头,确保用户已登录且有权访问相应租户的数据。

6.1.7. 阶段七:统一登出
  • 动作: 用户点击登出按钮,前端和后端同时清理会话,并通知零信任平台。
  • 需求 : 对应您的需求文档 第7条
  • 代码依据 : ZeroTrustAuthController.java, src/api/login.ts
  • 流程:
    • 前端调用 userStore.loginOut()
    • loginOut() 调用 logoutApi()
    • logoutApilocalStorage 中读取原始的零信任令牌 (zeroTrustToken),并将其作为请求头发给后端的 /admin-api/zerosystem/auth/logout 接口。
    • 后端的 ZeroTrustAuthControllerlogout 方法被调用。
    • 通知零信任平台 : 调用 zeroTrustAuthService.offlineToken(),向Mockoon发起 POST /.../token/change 请求,使零信任令牌下线。
    • 销毁本地会话 : 调用 oauth2TokenService.removeAccessToken(),从Redis中删除当前应用的 accessToken,使其失效。
    • 前端在 logoutApi 调用成功后,执行 removeToken(), removeZeroTrustToken() 等方法,清理浏览器本地存储 ,并重置 userStore 状态,最后跳转到登录页。

至此,整个单点登录与登出的闭环流程全部完成。

相关推荐
代码匠心3 小时前
从零开始学Flink:流批一体的执行模式
java·大数据·后端·flink·大数据处理
一只程序烽.3 小时前
java项目使用宝塔面板部署服务器nginx不能反向代理找到图片资源
java·服务器·nginx
Deryck_德瑞克4 小时前
IDEA编译时报错OOM的解决方案
java·ide·intellij-idea
JH30734 小时前
Idea中新建package包,变成了Directory
java·ide·intellij-idea
_extraordinary_4 小时前
Java SpringMVC(三)--- SpringMVC,SpringIoC&DI
java·开发语言
计算机徐师兄4 小时前
Java基于SpringBoot的智慧校园管理系统小程序【附源码、文档说明】
java·微信小程序·小程序·智慧校园管理系统小程序·java智慧校园管理系统小程序·智慧校园管理系统微信小程序·智慧校园管理微信小程序
考虑考虑4 小时前
ScopedValue在JDK24以及JDK25的改动
java·后端·java ee
itachi-uchiha5 小时前
关于Tomcat的页面后台管理默认设置
java·tomcat·firefox
骑着bug的coder5 小时前
RestTemplate 和 Feign 传参差异导致的接口调用失败
java