如何实现SignalR的异步IUserIdProvider



我有一个身份验证设置,我在声明中存储会话ID和未验证的用户ID。然后,在一个普通的控制器中,我会在数据库中查找会话,以验证它是否与userID匹配,并考虑用户登录

我正在尝试使用Azure SignalR。我希望能够通过userID向连接的用户发送消息,并且我需要实现IUserIdProvider。它上的GetUserId方法不是异步的,但我需要执行相同的逻辑,在它认为用户有效之前,它会根据数据库验证声明中的会话ID和用户ID。此代码将是异步的,但GetUserId方法不是异步的。

我有什么选择?

谢谢!

我认为IUserIdProvider.GetUserId(HubConnectionContext)不是异步的原因是对DB或其他外部资源的调用会在请求管道的早期发生。这可能包括将外部用户id或会话id映射到内部用户id之类的操作。

我解决类似问题的方法是将DB查找放在IClaimsTransformation实现中,该实现存储了我在流经请求的用户声明中查找的值。

public static class ApplicationClaimTypes
{
public static string UserId => "user-id";
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Identity.Web;

class MsalClaimsTransformation : IClaimsTransformation
{
// IMapExternalUsers represents the actions you must take to map an external id to an internal user id
private readonly IMapExternalUsers _externalUsers;
private ClaimsPrincipal _claimsPrincipal;
public MsalClaimsTransformation(IMapExternalUsers externalUsers)
{
_externalUsers = externalUsers;
}
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal claimsPrincipal)
{
_claimsPrincipal = claimsPrincipal;
// This check is important because the IClaimsTransformation may run multiple times in a single request
if (!claimsPrincipal.HasClaim(claim => claim.Type == ApplicationClaimTypes.UserId))
{
var claimsIdentity = await MapClaims();
claimsPrincipal.AddIdentity(claimsIdentity);
}
return claimsPrincipal;
}
private async Task<ClaimsIdentity> MapClaims()
{
var externalIds = new[]
{
// Extensions from Microsoft.Identity.Web
_claimsPrincipal.GetHomeObjectId(),
_claimsPrincipal.GetObjectId()
}
.Distinct()
.Where(id => !string.IsNullOrWhiteSpace(id))
.ToArray();
// Replace with implementation specific to your use case for mapping session/external 
// id to authenticated internal user id.
var userId = await _externalUsers.MapExternalIdAsync(externalIds);
var claimsIdentity = new ClaimsIdentity();
AddUserIdClaim(claimsIdentity, userId);
// Add other claims as needed
return claimsIdentity;
}

private void AddUserIdClaim(ClaimsIdentity claimsIdentity, Guid? userId)
{
claimsIdentity.AddClaim(new Claim(ApplicationClaimTypes.UserId, userId.ToString()));
}
}

一旦您将内部用户id作为声明存储在user对象中,SignalR的GetUserId()方法就可以访问它,而不需要异步代码。

using System.Security.Claims;
using Microsoft.AspNetCore.SignalR;
internal class SignalrUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
var httpContext = connection.GetHttpContext();
// If you have a multi-tenant application, you may have an 
// extension method like this to get the current tenant the user 
// is in.  Otherwise just remove it.
var tenantIdentifier = httpContext.GetTenantIdentifier();

var userId = connection.User.FindFirstValue(ApplicationClaimTypes.UserId);
if (string.IsNullOrWhiteSpace(userId))
{
return string.Empty;
}
return $"{tenantIdentifier}-{userId}";
}
}

相关内容

  • 没有找到相关文章

最新更新