跳转到主要内容

泛微 EC9 对接网易企业邮箱 SSO 最终实施说明

一、实施背景

为统一身份认证,减少多系统重复登录,现将泛微 EC9 系统与网易企业邮箱进行单点登录(SSO)对接。本文档用于指导 实施、部署、运维及后续维护,可作为项目交付与内部技术留档。

二、整体技术架构

2.1 系统角色

  • 泛微 EC9:身份源(SSO 发起方)
  • 网易企业邮箱开放平台:SSO 接收方
  • 浏览器:跳转载体

2.2 SSO 时序流程

  1. 用户登录泛微 EC9
  2. 用户点击“网易邮箱”入口
  3. EC9 后端调用网易接口获取 ssoAuthToken
  4. EC9 后端调用 ssoSign 接口生成一次性 sso_token
  5. 浏览器 redirect 跳转网易邮箱
  6. 用户免登录进入邮箱系统

三、前置条件

3.1 泛微侧

  • EC9 系统已正常部署
  • 用户已通过泛微统一认证登录
  • 已确认使用 loginid 作为邮箱账号前缀

3.2 网易邮箱侧

  • 已开通企业邮箱
  • 已在“网易企业邮箱开放平台”创建应用
  • 已启用 单点登录(SSO)权限
  • 获取以下信息:
    • appId
    • appSecret
    • 企业邮箱主域名(如:shineclub.cn)

四、关键技术约定

4.1 用户账号映射规则

泛微字段 网易邮箱字段
loginid accountName

示例:

泛微 loginid = heqp
邮箱账号 = heqp@shineclub.cn

4.2 重要约束(必须遵守)

  • Header 中 必须使用qiye-app-id
  • qiye-sso-auth-token 禁止 URL 编码
  • SSO 跳转 URL 禁止重复拼接 https://
  • JSP 中 禁止在 redirect 前使用 out.print

五、核心实现说明

5.1 获取当前登录用户(EC9)

<%@ page import="weaver.hrm.User" %>
<%@ page import="weaver.hrm.HrmUserVarify" %>

HrmUserVarify.checkUser(request, response);
User user = HrmUserVarify.getUser(request, response);
String accountName = user.getLoginid();

5.2 调用网易 SSO 接口(后端)

请求头(必须)

qiye-app-id: <appId>
qiye-sso-auth-token: <原始 token,不可 encode>
Content-Type: application/json

关键接口

/api/pub/token/ssoAuthToken
/api/sso/ssoSign

5.3 最终跳转 URL 构造

String redirectUrl = endpoint + "?sso_token=" + sign + "&lang=0";
response.sendRedirect(redirectUrl);

示例: https://entry.qiye.163.com/login/ssoLogin?sso_token=xxxxx&lang=0

六、JSP 实施示例(生产可用)

<%@ page import="weaver.hrm.User" %>
<%@ page import="weaver.hrm.HrmUserVarify" %>
<%@ page import="com.weaver.general.BaseBean" %>
<%@ page import="com.api.shineclub.netease.*" %>

<%
  BaseBean log = new BaseBean();
  try {
    HrmUserVarify.checkUser(request, response);
    User user = HrmUserVarify.getUser(request, response);

    String accountName = user.getLoginid();
    log.writeLog("【网易邮箱SSO】loginid=" + accountName);

    String ssoUrl = new NeteaseSSOService()
      .buildSSOUrl(accountName, NeteaseConfig.domain);

    response.sendRedirect(ssoUrl);
    return;

  } catch (Exception e) {
    log.writeLog("【网易邮箱SSO】异常", e);
    out.print("网易邮箱单点登录失败:" + e.getMessage());
  }
%>

七、日志与排错指引

7.1 推荐日志点

  • 当前 loginid
  • ssoAuthToken 是否获取成功
  • ssoSign 接口响应
  • 最终 redirectUrl

7.2 常见问题对照

报错信息 原因 解决方案
appId 不能为空 Header 名错误 使用 qiye-app-id
sso token 验证失败 token 被 encode 禁止编码
页面无法打开 URL 拼接错误 endpoint 原样使用
weaver_user 为 null 未校验登录 使用 HrmUserVarify

八、安全与运维建议

  • SSO 接口仅部署在内网
  • 日志中避免打印完整 token
  • 定期检查网易应用权限有效性
  • 建议配置失败兜底(跳普通登录)

九、常见问题 FAQ 与排错流程

9.1 常见问题 FAQ

Q1:访问 SSO 页面提示 appId 不能为空

A:请求头使用了错误的 Header 名。必须使用:

qiye-app-id

而不是 appId

Q2:提示 sso token 验证失败 (-321)

Aqiye-sso-auth-token 被 URL 编码或被篡改。Header 中的 token 必须原样传输,禁止 URLEncoder.encode

Q3:可以成功跳转,但网易邮箱页面打不开?

A:SSO 跳转 URL 拼接错误。endpoint 本身已包含 https://,禁止再次拼接协议头。

Q4:JSP 中获取不到当前登录用户(weaver_user 为 null)?

A:JSP 未经过泛微登录校验。必须在 JSP 中调用:

HrmUserVarify.checkUser(request, response);

Q5:页面无任何反应或偶发跳转失败?

A:在 response.sendRedirect() 前使用了 out.print,导致响应提前提交。生产环境中禁止二者同时使用。

9.2 排错流程(推荐顺序)

1. 确认泛微已登录

  • 首页可正常访问
  • JSP 中 HrmUserVarify.checkUser 不跳转登录页

2. 确认 loginid 获取正确

  • 日志打印 user.getLoginid()

3. 确认 ssoAuthToken 接口返回 success=true

4. 确认 ssoSign 接口 Header

  • 是否包含 qiye-app-id
  • token 是否未编码

5. 检查最终 redirectUrl

  • 是否只有一个 https://
  • 是否包含 sso_token

6. 浏览器直接访问 redirectUrl 测试

十、附录

A. 关键类说明

  • NeteaseSSOService:封装网易 SSO 接口调用
  • NeteaseConfig:集中配置类
  • HrmUserVarify:泛微登录态校验