using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Yh.Platform.Auth; namespace YhCore.WebApi.Auth { /// /// 轻量 CAS service ticket 校验器(对齐 Java CasServiceTicketValidator,不依赖 CAS 客户端库)。 /// 调用 CAS 的 /serviceValidate 端点,从响应 XML 中提取 <cas:user>。 /// public class CasServiceTicketValidator { private readonly string _casServerUrl; private readonly int _timeoutMs; public CasServiceTicketValidator(string casServerUrl, int timeoutMs = 5000) { _casServerUrl = casServerUrl; _timeoutMs = timeoutMs > 0 ? timeoutMs : 5000; } /// 校验 ticket,成功返回登录账号,失败/无效返回 null。 public async Task ValidateAsync(string ticket, string service, CancellationToken ct = default) { if (string.IsNullOrWhiteSpace(_casServerUrl)) { throw new AuthException("未配置 CasServerUrlPrefix(yh:sso:cas-server-url-prefix),无法进行 ticket 校验"); } var base_ = _casServerUrl.EndsWith('/') ? _casServerUrl : _casServerUrl + "/"; var endpoint = base_ + "serviceValidate" + "?ticket=" + Uri.EscapeDataString(ticket ?? "") + "&service=" + Uri.EscapeDataString(service ?? ""); try { using var handler = new SocketsHttpHandler { ConnectTimeout = TimeSpan.FromMilliseconds(_timeoutMs) }; using var http = new HttpClient(handler) { Timeout = TimeSpan.FromMilliseconds(_timeoutMs) }; using var resp = await http.GetAsync(endpoint, ct).ConfigureAwait(false); var body = await resp.Content.ReadAsStringAsync(ct).ConfigureAwait(false); if (!resp.IsSuccessStatusCode || body == null) { return null; } return ExtractUser(body); } catch (Exception e) when (e is HttpRequestException or TaskCanceledException) { throw new AuthException("CAS serviceValidate 请求失败: " + e.Message, e); } } /// /// 从 CAS 响应 XML 提取用户名。 /// 成功响应形如 <cas:serviceResponse><cas:authenticationSuccess><cas:user>NAME</cas:user>... /// public static string ExtractUser(string xml) { if (string.IsNullOrEmpty(xml)) { return null; } var start = xml.IndexOf("", StringComparison.Ordinal); if (start < 0) { start = xml.IndexOf("', start); var end = xml.IndexOf("", tagEnd, StringComparison.Ordinal); if (tagEnd < 0 || end < 0) { return null; } return xml.Substring(tagEnd + 1, end - tagEnd - 1).Trim(); } } }