我正在使用基于令牌的授權制作基于 SignalR 的服務。令牌由外部 API 驗證,該 API 回傳用戶的 ID。然后,通知會發送給具有特定 ID 的用戶。
我無法解決的問題是 SignalR 的客戶端代碼顯然發送了 2 個請求:一個沒有令牌(身份驗證失敗),另一個帶有令牌(身份驗證成功)。出于某種原因,第一個結果被快取,用戶沒有收到任何通知。
如果我注釋檢查并始終回傳正確的 ID,即使沒有指定令牌,代碼也會突然開始作業。
HubTOkenAuthenticationHandler.cs:
public class HubTokenAuthenticationHandler : AuthenticationHandler<HubTokenAuthenticationOptions>
{
public HubTokenAuthenticationHandler(
IOptionsMonitor<HubTokenAuthenticationOptions> options,
ILoggerFactory logFactory,
UrlEncoder encoder,
ISystemClock clock,
IAuthApiClient api
)
: base(options, logFactory, encoder, clock)
{
_api = api;
}
private readonly IAuthApiClient _api;
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
try
{
// uncommenting this line makes everything suddenly work
// return SuccessResult(1);
var token = GetToken();
if (string.IsNullOrEmpty(token))
return AuthenticateResult.NoResult();
var userId = await _api.GetUserIdAsync(token); // always returns 1
return SuccessResult(userId);
}
catch (Exception ex)
{
return AuthenticateResult.Fail(ex);
}
}
/// <summary>
/// Returns an identity with the specified user id.
/// </summary>
private AuthenticateResult SuccessResult(int userId)
{
var identity = new ClaimsIdentity(
new[]
{
new Claim(ClaimTypes.Name, userId.ToString())
}
);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
/// <summary>
/// Checks if there is a token specified.
/// </summary>
private string GetToken()
{
const string Scheme = "Bearer ";
var auth = Context.Request.Headers["Authorization"].ToString() ?? "";
return auth.StartsWith(Scheme)
? auth.Substring(Scheme.Length)
: "";
}
}
啟動.cs:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHostedService<FakeNotificationService>();
services.AddSingleton<IAuthApiClient, FakeAuthApiClient>();
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
services.AddAuthentication(opts =>
{
opts.DefaultAuthenticateScheme = HubTokenAuthenticationDefaults.AuthenticationScheme;
opts.DefaultChallengeScheme = HubTokenAuthenticationDefaults.AuthenticationScheme;
})
.AddHubTokenAuthenticationScheme();
services.AddRouting(opts =>
{
opts.AppendTrailingSlash = false;
opts.LowercaseUrls = false;
});
services.AddSignalR(opts => opts.EnableDetailedErrors = true);
services.AddControllers();
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseRouting();
app.UseAuthentication();
app.UseEndpoints(x =>
{
x.MapHub<InfoHub>("/signalr/info");
x.MapControllers();
});
}
}
FakeNotificationsService.cs(每 2 秒向用戶“1”發送一次通知):
public class FakeNotificationService: IHostedService
{
public FakeNotificationService(IHubContext<InfoHub> hubContext, ILogger<FakeNotificationService> logger)
{
_hubContext = hubContext;
_logger = logger;
_cts = new CancellationTokenSource();
}
private readonly IHubContext<InfoHub> _hubContext;
private readonly ILogger _logger;
private readonly CancellationTokenSource _cts;
public Task StartAsync(CancellationToken cancellationToken)
{
// run in the background
Task.Run(async () =>
{
var id = 1;
while (!_cts.Token.IsCancellationRequested)
{
await Task.Delay(2000);
await _hubContext.Clients.Users(new[] {"1"})
.SendAsync("NewNotification", new {Id = id, Date = DateTime.Now});
_logger.LogInformation("Sent notification " id);
id ;
}
});
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_cts.Cancel();
return Task.CompletedTask;
}
}
Debug.cshtml(客戶端代碼):
<html>
<head>
<title>SignalRPipe Debug Page</title>
</head>
<body>
<h3>Notifications log</h3>
<textarea id="log" cols="180" rows="40"></textarea>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/5.0.11/signalr.min.js"
integrity="sha512-LGhr8/QqE/4Ci4RqXolIPC H9T0OSY2kWK2IkqVXfijt4aaNiI8/APVgji3XWCLbE5J0wgSg3x23LieFHVK62g=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script language="javascript">
var token = "123";
var conn = new signalR
.HubConnectionBuilder()
.withUrl('/signalr/info', { accessTokenFactory: () => token })
.configureLogging(signalR.LogLevel.Debug)
.build();
var logElem = document.getElementById('log');
var id = 1;
function log(text) {
logElem.innerHTML = text '\n\n' logElem.innerHTML;
}
conn.on("NewNotification", alarm => {
log(`[Notification ${id}]:\n${JSON.stringify(alarm)}`);
id ;
});
conn.start()
.then(() => log('Connection established.'))
.catch(err => log(`Connection failed:\n${err.toString()}`));
</script>
</body>
</html>
作為可運行專案的最小重現:https : //github.com/impworks/signalr-auth-problem
我嘗試了以下方法,但沒有成功:
- 添加一個假授權處理程式,它只允許一切
- 將除錯視圖提取到單獨的專案(基于 express.js 的服務器)
我在這里缺少什么?
uj5u.com熱心網友回復:
看起來您沒有處理來自查詢字串的身份驗證令牌,這在某些情況下是必需的,例如來自瀏覽器的 WebSocket 連接。
有關如何處理承載身份驗證的一些資訊,請參閱https://docs.microsoft.com/aspnet/core/signalr/authn-and-authz?view=aspnetcore-5.0#built-in-jwt-authentication。
uj5u.com熱心網友回復:
問題解決了。正如@Brennan 正確猜測的那樣,WebSockets 不支持標頭,因此令牌通過查詢字串傳遞。我們只需要一些代碼即可從任一來源獲取令牌:
private string GetHeaderToken()
{
const string Scheme = "Bearer ";
var auth = Context.Request.Headers["Authorization"].ToString() ?? "";
return auth.StartsWith(Scheme)
? auth.Substring(Scheme.Length)
: null;
}
private string GetQueryToken()
{
return Context.Request.Query["access_token"];
}
然后,在HandleAuthenticateAsync:
var token = GetHeaderToken() ?? GetQueryToken();
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/336620.html
上一篇:如何為GridRowDefinition高度變化設定影片(當Height="Auto"時)
下一篇:我正在嘗試在Django中的auth_user中創建用戶,但我在/register/set_password()處出現此錯誤TypeErrormissing1required
