#c# #asp.net-core #authentication #signalr #bearer-token
Вопрос:
У меня есть ASP.NET 5 Веб-приложение, которое является частью более крупной системы и использует аутентификацию файлов cookie для запросов браузера.
Я хочу добавить возможность запрашивать данные и выполнять определенные действия в определенных службах Windows, которые также являются частью общей системы и выполняются на нескольких отдельных компьютерах. Я хочу использовать для этого SignalR. Затем службы Windows запускаются как выделенный идентификатор службы, который является частью нашего ActiveDirectory. Поскольку службы не должны хранить свои учетные данные пользователя в коде или локальных файлах конфигурации, они запрашивают маркер аутентификации для веб-приложения из API, который работает с проверкой подлинности Windows.
Затем, при установлении соединения SignalR с веб-приложением, службы будут использовать токен, полученный от API, для аутентификации в веб-приложении. В целом это работает.
Конфигурация аутентификации веб — приложения является:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Login";
options.ExpireTimeSpan = TimeSpan.FromMinutes(12);
options.SlidingExpiration = true;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, opt =>
{
// Configuration details excluded
// ...
opt.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// ...
}
};
Согласно документации Microsoft, это должна быть конфигурация аутентификации vaild.
В services.AddAuthorization(...)
методе я добавил политику, специфичную для схемы на предъявителя:
options.AddPolicy("SignalRService", policy =>
{
policy.RequireRole("SignalRService");
policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
});
И затем существует метод концентратора SignalR, защищенный этой политикой:
[Authorize(Policy = "SignalRService")]
public async Task RegisterService(string clientIdString) { /*...*/ }
And finally the hub connection in the windows service is created as follows:
connection = new HubConnectionBuilder()
.WithUrl(hubAddress, options =>
{
options.AccessTokenProvider = () => Task.FromResult(authToken);
})
.WithAutomaticReconnect()
.Build();
Establishing the connection works:
await connection.StartAsync();
Но когда я пытаюсь вызвать метод концентратора из службы Windows, например await connection.InvokeAsync("RegisterService", clientId);
, я получаю HubException
сообщение:
Не удалось вызвать «registerService», потому что пользователь не авторизован
Я также создал контроллер API в веб-приложении для целей тестирования и обеспечил его той же политикой:
[HttpGet]
[Authorize(Policy = "SignalRService")]
public IActionResult Get()
{
return Ok(User.Identity.Name);
}
Когда я вызываю эту конечную точку API с тем же маркером, который я использовал бы для вызова концентратора SignalR, я получаю идентификатор, установленный для возвращенного токена, как и ожидалось. Я также проверил, что настроенный OnMessageReceived
обработчик событий выполняется в этом сценарии, в то время как это не так, когда я использую соединение SignalR.
Когда я устанавливаю JwtBearerDefaults.AuthenticationScheme
в качестве схемы по умолчанию Startup.cs
вместо CookieAuthenticationDefaults.AuthenticationScheme
этого, она также работает с концентратором SignalR, но затем моя стандартная аутентификация пользователя на основе файлов cookie прерывается.
Я ожидаю, что существует некоторая дополнительная конфигурация, необходимая для того, чтобы веб-приложение явно использовало схему носителя при вызове метода концентратора, но пока я ничего не смог найти.
Ответ №1:
После отчаянных попыток в течение еще одного часа я обнаружил, что конкретная аутентификация на предъявителя работала с аутентификацией файлов cookie по умолчанию, когда я ставил Authorize(Policy = "SignalRService")
ее непосредственно в класс, а не в метод.
Поскольку мой концентратор также должен быть доступен для подключений к браузеру с использованием файлов cookie, я, наконец, получил:
[Authorize(AuthenticationSchemes = "Bearer,Cookies")]
public class SignalRServiceHub : Hub
{
[Authorize(Policy = "SignalRService")]
public async Task RegisterService(string clientIdString)
{
// ...
}
[Authorize(Policy = "Root")]
public async Task RegisterMonitoringClient()
{
// ...
}
Я не совсем уверен, почему в этом случае необходимо указывать схемы на уровне класса, в то время как это не относится к ApiController
реализациям