From ff31293df3748d61a85f66b5223cf8eb087fffa6 Mon Sep 17 00:00:00 2001 From: wanghu Date: Fri, 4 Sep 2026 12:23:02 +0800 Subject: [PATCH] =?UTF-8?q?service=20=E5=9B=9E=E8=B7=B3=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=20Java=20notice=20=E5=88=86=E5=8F=B7?= =?UTF-8?q?=E7=9F=A9=E9=98=B5=E6=AE=B5=E5=BD=A2=E6=80=81=EF=BC=9A{path};re?= =?UTF-8?q?directToReferer=3D[=3Fquery]=EF=BC=88=E5=BC=83=20query=20?= =?UTF-8?q?=E5=BD=A2=E6=80=81=EF=BC=8C=E5=9B=9E=E8=B0=83=E7=BB=8F=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E5=88=86=E5=8F=B7=E6=AE=B5=E8=A7=A3=E6=9E=90=EF=BC=9B?= =?UTF-8?q?query=20=E6=97=A7=E5=BD=A2=E6=80=81=E4=BB=8D=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Auth/Cas/CasAuthenticationHandler.cs | 36 +++++++++++-------- .../Middleware/SsoProtectionMiddleware.cs | 11 +++--- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/YhCore.WebApi/Auth/Cas/CasAuthenticationHandler.cs b/src/YhCore.WebApi/Auth/Cas/CasAuthenticationHandler.cs index 94c11a4..4bac20d 100644 --- a/src/YhCore.WebApi/Auth/Cas/CasAuthenticationHandler.cs +++ b/src/YhCore.WebApi/Auth/Cas/CasAuthenticationHandler.cs @@ -19,14 +19,16 @@ namespace YhCore.WebApi.Auth /// Challenge:302 到 CAS 登录页,service = {AppBaseUrl}{当前 path}[?除 ticket 外 query],即原访问 URL, /// 与 Java notice 的 service 形态一致(service=http://host/module/api/xxx);AppBaseUrl 含模块前缀 /// (如 /docprint)时拼接结果天然带模块名。显式回跳语境(前端 yschreferer/Referer 携带页面地址)时 - /// 在该 service 上追加 query 参数 redirectToReferer=<hex(回跳地址)> 携带「登录后回页面」信息—— - /// 不再以 {SignInCallbackPath} 回调端点作 service(signin-cas 形态已弃用);hex 按 char 维度编码 - /// (对齐前端 strToHexCharCode / Java HexUtils),避免 query 内嵌完整 URL 需多层 urlencode。 - /// ASP.NET Core 无 Java 路径矩阵参数机制,回跳目标只能放 query。 + /// 把回跳地址以 Java notice 同款分号矩阵段插入 path 与 query 之间: + /// {path};redirectToReferer=<hex(回跳地址)>[?{原 query}]——不再以 {SignInCallbackPath} 回调端点作 + /// service(signin-cas 形态已弃用);hex 按 char 维度编码(对齐前端 strToHexCharCode / Java HexUtils), + /// 分号段不参与 query 结构,含中文/特殊字符的页面地址也可无歧义承载。ASP.NET Core 对回调中分号段 + /// 的处理:带 ticket 校验成功后由中间件短路(不落 MVC 路由),路径分号段仅用于解析回跳目标。 /// Authenticate:任意受保护路径带 ticket 的回调均按「path + 除 ticket 外的 query」重建 service - /// 调 /serviceValidate 校验(service 即登录时的原访问 URL,CAS 在末尾追加 ?ticket=/&ticket= 回跳)。 - /// 成功后写入 Cookie 登录态并 302 到 redirectToReferer(hex 解码,当前形态)/ returnUrl(旧中继形态 - /// 兼容)/ 原 URL 去 ticket 版(无回跳目标),兼容更早的 ;redirectToReferer=<hex> 路径段形态。 + /// 调 /serviceValidate 校验(service 即登录时的原访问 URL,含分号段则一并重建,CAS 在末尾追加 + /// ?ticket=/&ticket= 回跳)。成功后写入 Cookie 登录态并 302 到分号段 redirectToReferer(hex 解码, + /// 当前形态)/ query redirectToReferer(旧 query 形态兼容)/ returnUrl(更早中继形态兼容)/ + /// 原 URL 去 ticket 版(无回跳目标)。 /// /// public class CasAuthenticationHandler : AuthenticationHandler @@ -36,7 +38,8 @@ namespace YhCore.WebApi.Auth /// 旧中继形态(service={回调端点}?returnUrl=…)的回跳参数——已弃用,仅兼容历史 URL/缓存。 private const string ReturnUrlParam = "returnUrl"; - /// 当前形态回跳参数:service=原访问 URL 时追加 ?redirectToReferer=<hex(回跳地址)>,登录后 CAS 原样回带。 + /// 旧 query 形态(service=原访问 URL?redirectToReferer=<hex>,986b241 曾用)的回跳参数—— + /// 已改分号矩阵段承载,此处仅兼容读取历史 URL/缓存。 private const string RedirectToRefererQueryParam = "redirectToReferer"; private readonly CasServiceTicketValidator _validator; @@ -128,13 +131,18 @@ namespace YhCore.WebApi.Auth else { // 显式回跳语境(前端 yschreferer 携带页面地址等):service 仍 = 原访问 URL(notice 形态, - // 不再以 {SignInCallbackPath} 回调端点作 service),仅追加 query 参数 - // redirectToReferer= 携带「登录后回页面」信息——CAS 会把它作为 service 一部分 - // 在回调时原样带回,本 handler 校验成功后 hex 解码即得回跳目标。hex 按 char 维度编码,避免 - // query 内嵌完整 URL 需多层 urlencode(对齐前端 strToHexCharCode / Java HexUtils)。 + // 不再以 {SignInCallbackPath} 回调端点作 service),把回跳地址以 Java notice 同款的分号矩阵段 + // 形式插入 path 与 query 之间:{path};redirectToReferer=[?{原 query}]——CAS 会把它作为 + // service 一部分在回调时原样带回,本 handler 校验成功后由 ParseRedirectToReferer 从路径段 + // hex 解码即得回跳目标(query 形态 ?redirectToReferer= 的旧 URL/缓存仍兼容读取)。 + // hex 按 char 维度编码(对齐前端 strToHexCharCode / Java HexUtils);分号段内不参与 query 结构, + // 含中文/特殊字符的页面地址也可无歧义承载。 service = BuildServiceUrlFromRequest(); - service += (service.IndexOf('?') < 0 ? "?" : "&") - + RedirectToRefererQueryParam + "=" + HexEncode(target); + var q = service.IndexOf('?'); + var segment = RedirectToRefererMarker + HexEncode(target); + service = q < 0 + ? service + segment + : service.Substring(0, q) + segment + service.Substring(q); } // Uri.EscapeDataString 会把 ; / : = 全部编码(%3B/%2F/%3A/%3D),与平台旧版系统生成的 // CAS 登录 URL 逐字节一致(UrlEncoder.Encode 会保留 ; 字面量) diff --git a/src/YhCore.WebApi/Middleware/SsoProtectionMiddleware.cs b/src/YhCore.WebApi/Middleware/SsoProtectionMiddleware.cs index 2e853d1..3192158 100644 --- a/src/YhCore.WebApi/Middleware/SsoProtectionMiddleware.cs +++ b/src/YhCore.WebApi/Middleware/SsoProtectionMiddleware.cs @@ -111,8 +111,9 @@ namespace YhCore.WebApi.Middleware /// /// 触发 Challenge 生成 CAS 登录地址(service 构造由本地化 CasAuthenticationHandler 完成: - /// service 恒为原访问 URL(notice 形态);显式回跳(yschreferer/Referer)时仅在其上追加 - /// redirectToReferer=<hex> query,不再以 SignInCallbackPath 回调端点作 service)。 + /// service 恒为原访问 URL(notice 形态);显式回跳(yschreferer/Referer)时以 Java notice 同款 + /// 分号矩阵段 ;redirectToReferer=<hex> 插入 path 与 query 之间,不再以 SignInCallbackPath + /// 回调端点作 service)。 /// 受保护 API 的 AJAX/XHR 请求:拦截框架写入的 302 + Location,改写为 200 + JSON,避免 XHR /// 跟随跨域 302 跳转 CAS 登录页时被 CORS 拦截;文档导航 / 根路径 / 回调仍保持 302。 /// @@ -127,9 +128,9 @@ namespace YhCore.WebApi.Middleware // (null)→ handler 以纯净"原访问 URL"作 service(notice 形态 service=http://host/module/api/xxx), // 登录后 CAS 回跳原 URL+ticket,由 handler 校验并 302 去掉 ticket 落回原地址; // - 显式回跳语境(前端 yschreferer 打标携带页面地址 / Referer):回跳目标 = 页面地址, - // handler 仍以原访问 URL 作 service 主体并追加 ?redirectToReferer=<hex(目标)> 携带回页面 - // 信息(ASP.NET Core 无 Java 路径矩阵参数机制,只能放 query)——前端登录后回到所在页面 - // 而非接口本身; + // handler 仍以原访问 URL 作 service 主体,并把目标以分号矩阵段 ;redirectToReferer=<hex> + // 插入 path 与 query 之间(与 Java notice 同款;分号段不参与 query 结构,目标含中文/特殊 + // 字符也无歧义)——前端登录后回到所在页面而非接口本身; // - 回调路径直访防呆(访问的恰是回调端点自身且无 ticket 未认证、无打标):回 "/", // 避免登录后回跳回调自身再触发 Challenge(前端打标场景仍回前端页——回调路径本身常是前端 // 登录检测接口 /api/base/GetUserInfo,必须回到用户所在页面)。