
最近在练习做一个 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 中心并根据应用程序的独特要求定制身份验证过程。此方法允许对令牌验证和声明提取进行细粒度控制,从而确保安全可靠的实时通信系统。
欢迎随意扩展此实现,添加额外的验证、日志记录或与外部身份提供商的集成,以获得更全面的解决方案。
如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。