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();
}
}
}