@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Yh.Platform.Auth;
|
||||
|
||||
namespace YhCore.WebApi.Auth
|
||||
{
|
||||
/// <summary>
|
||||
/// 轻量 CAS service ticket 校验器(对齐 Java CasServiceTicketValidator,不依赖 CAS 客户端库)。
|
||||
/// 调用 CAS 的 /serviceValidate 端点,从响应 XML 中提取 <cas:user>。
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>校验 ticket,成功返回登录账号,失败/无效返回 null。</summary>
|
||||
public async Task<string> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 CAS 响应 XML 提取用户名。
|
||||
/// 成功响应形如 <cas:serviceResponse><cas:authenticationSuccess><cas:user>NAME</cas:user>...
|
||||
/// </summary>
|
||||
public static string ExtractUser(string xml)
|
||||
{
|
||||
if (string.IsNullOrEmpty(xml))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var start = xml.IndexOf("<cas:user>", StringComparison.Ordinal);
|
||||
if (start < 0)
|
||||
{
|
||||
start = xml.IndexOf("<cas:user ", StringComparison.Ordinal); // 容错带属性
|
||||
}
|
||||
if (start < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var tagEnd = xml.IndexOf('>', start);
|
||||
var end = xml.IndexOf("</cas:user>", tagEnd, StringComparison.Ordinal);
|
||||
if (tagEnd < 0 || end < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return xml.Substring(tagEnd + 1, end - tagEnd - 1).Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user