最近在練習做一個 Web 開發項目,需要使用 WebSockets 傳輸數據,實現實時通信。這是一個 React.js 項目,后端是 .NET。
雖然 MSDN 提供了出色的頂級文檔,但它通常缺少高級用例所需的低級細節。
一種這樣的場景是使用自定義令牌對 SignalR Hub 進行身份驗證。是的,自定義令牌,而不是 JWT 或默認 Bearer 令牌。本文探討如何實現這一點。最后,您將擁有一個需要身份驗證并使用自定義令牌的 SignalR Hub。
自定義Token
我們將使用的自定義令牌是 Base64 編碼的用戶信息分隔字符串,格式如下:
userId:userName
從這個標記中,我們將提取userId并userName創建聲明。
項目設置
以下是設置項目的基本步驟:
創建一個.NET項目:
dotnet new webapi
添加 SignalR 服務:
在Program.cs文件中,構建應用程序時注冊 SignalR 服務:
builder.Services.AddSignalR();
創建 Hub :
創建一個名為 的目錄hubs,并添加一個名為 的文件GameHub.cs。執行以下操作:
public class GameHub : Hub
{
? ?public override Task OnConnectedAsync()
? ?{
? ? ? ?return base.OnConnectedAsync();
? ?}
? ?public override Task OnDisconnectedAsync(Exception? exception)
? ?{
? ? ? ?return base.OnDisconnectedAsync(exception);
? ?}
}
映射中心:
將其GameHub作為端點公開Program.cs:
app.MapHub<GameHub>("/hubs/game");
實現自定義令牌認證
要使用自定義令牌并從中提取用戶信息,我們需要一個自定義身份驗證方案。在 .NET 中,身份驗證方案是一個命名標識符,它指定用于對用戶進行身份驗證的方法或協議,例如 Cookie、JWT 持有者令牌或 Windows 身份驗證。對于此場景,我們將創建一個名為的方案CustomToken。
自定義身份驗證方案實現
定義自定義令牌方案選項:
public class CustomTokenSchemeOptions : AuthenticationSchemeOptions
{
? ?public CustomTokenSchemeOptions()
? ?{
? ? ? ?Events = new CustomTokenEvents();
? ?}
? ?public new CustomTokenEvents Events
? ?{
? ? ? ?get => (CustomTokenEvents)base.Events!;
? ? ? ?set => base.Events = value;
? ?}
}
定義方案處理程序:包含驗證令牌和提取用戶聲明的邏輯
:CustomTokenSchemeHandler
public class CustomTokenSchemeHandler : AuthenticationHandler<CustomTokenSchemeOptions>
{
? ?private new CustomTokenEvents Events => (CustomTokenEvents)base.Events!;
? ?public CustomTokenSchemeHandler(
? ? ? ?IOptionsMonitor<CustomTokenSchemeOptions> options,
? ? ? ?ILoggerFactory logger,
? ? ? ?UrlEncoder encoder) : base(options, logger, encoder) {}
? ?protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
? ?{
? ? ? ?var messageReceivedContext = new MessageReceivedContext(Context, Scheme, Options);
? ? ? ?await Events.MessageReceivedAsync(messageReceivedContext);
? ? ? ?var token = messageReceivedContext.Token ?? GetTokenFromQuery();
? ? ? ?if (token is null)
? ? ? ?{
? ? ? ? ? ?return AuthenticateResult.NoResult();
? ? ? ?}
? ? ? ?byte[] data = Convert.FromBase64String(token);
? ? ? ?string decodedString = Encoding.UTF8.GetString(data);
? ? ? ?string[] userInfoArray = decodedString.Split(":");
? ? ? ?var claims = new[]
? ? ? ?{
? ? ? ? ? ?new Claim(ClaimTypes.Name, userInfoArray[1]),
? ? ? ? ? ?new Claim(ClaimTypes.Sid, userInfoArray[0])
? ? ? ?};
? ? ? ?var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Name));
? ? ? ?var ticket = new AuthenticationTicket(principal, Scheme.Name);
? ? ? ?return AuthenticateResult.Success(ticket);
? ?}
? ?private string? GetTokenFromQuery()
? ?{
? ? ? ?var accessToken = Context.Request.Query["access_token"].ToString();
? ? ? ?return string.IsNullOrEmpty(accessToken) ? null : accessToken;
? ?}
}
配置身份驗證方案:
在以下位置注冊自定義身份驗證方案Program.cs:
builder.Services.AddAuthentication("CustomToken")
? ?.AddScheme<CustomTokenSchemeOptions, CustomTokenSchemeHandler>("CustomToken", opts =>
? ?{
? ? ? ?opts.Events = new CustomTokenEvents
? ? ? ?{
? ? ? ? ? ?OnMessageReceived = context =>
? ? ? ? ? ?{
? ? ? ? ? ? ? ?var accessToken = context.Request.Query["access_token"];
? ? ? ? ? ? ? ?var path = context.HttpContext.Request.Path;
? ? ? ? ? ? ? ?if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs/game"))
? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ?context.Token = accessToken;
? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ?return Task.CompletedTask;
? ? ? ? ? ?};
? ? ? ?};
? ?});
免責聲明
本文介紹的實現靈感來自 .NET 官方存儲庫中的 BearerTokenScheme 源代碼。我們對其進行了調整以適應此場景的自定義令牌要求。
總結
通過實施此自定義令牌身份驗證方案,您可以保護 SignalR 中心并根據應用程序的獨特要求定制身份驗證過程。此方法允許對令牌驗證和聲明提取進行細粒度控制,從而確保安全可靠的實時通信系統。
歡迎隨意擴展此實現,添加額外的驗證、日志記錄或與外部身份提供商的集成,以獲得更全面的解決方案。
如果您喜歡此文章,請收藏、點贊、評論,謝謝,祝您快樂每一天。?