#asp.net-core #jwt #asp.net-core-webapi
#asp.net-ядро #jwt #asp.net-core-webapi
Вопрос:
Я использую JWT в ASP.NET Основной веб-api (.NET Core 3.1) с Microsoft.AspNetCore.Идентификация. У меня есть два проекта, настроенных следующим образом:
MyProj.Идентификатор и MyProj.Server. Я определил 2 роли: «администратор» и «пользователь». Я хотел бы авторизовать вызовы контроллеров MyProj.Server с помощью токенов, сгенерированных MyProj.Идентификация для пользователей в роли «пользователь». Я также хотел бы разрешить некоторые вызовы MyProj.Идентификация с токенами, сгенерированными MyProj.Идентификация пользователей в роли «администратор». В MyProj.У меня есть следующий контроллер:
namespace MyProj.Identity.Controllers
{
[ApiController]
[Route("api/authentication")]
public class AuthenticationController : ControllerBase
{
//...
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginDto model)
{
//...
}
[Authorize(Roles = UserRoles.Admin)]
[HttpPost("register")]
public async Task<IActionResult> Register([FromBody] RegistrationInfo registrationInput)
{
//...
}
[Authorize(Roles = UserRoles.Admin)]
[HttpPost("register-admin")]
public async Task<IActionResult> RegisterAdmin([FromBody] RegistrationInfo registrationInput)
{
//...
}
}
}
В MyProj.Server есть контроллеры, подобные следующим:
namespace MyProj.Server.Controllers
{
[Authorize(Roles = UserRoles.User)]
[ApiController]
[Route("api/items")]
public class MyController : ControllerBase
{
//...
[HttpGet]
public async Task<ActionResult<IEnumerable<ItemDto>>> Get()
{
//...
}
//...
}
}
Запуск в обоих проектах имеет следующую конфигурацию
Запуск
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
// Adding Jwt Bearer
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["JWT:ValidIssuer"],
ValidAudiences = Configuration.GetSection("JWT:ValidAudiences").Get<string[]>(),
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Secret"]))
};
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
//...
}
В appsettings.json для обоих проектов есть:
"JWT": {
"ValidIssuer": "http://MyProj.Identity.Url",
"ValidAudiences": [
"http://MyProj.Identity.Url",
"http://MyProj.Server.Url"
],
"Secret": "MySecret",
"TokenExpiryMinutes": "120"
}
Проблема, с которой я сталкиваюсь, заключается в том, что когда я устанавливаю свойство ValidAudiences в TokenValidationParameters, токен, который генерируется при входе в систему, не имеет никакого набора «aud» (проверяя его с помощью https://jwt.io /) и, как следствие, авторизация не выполняется. Если вместо этого я установлю свойство ValidAudience (одна аудитория), все работает нормально, но я не могу авторизовать более одной аудитории. Я попытался установить свойство «ValidAudiences» вручную также из списка (не из конфигурации), но результат был тот же. Кажется, я что-то упускаю в отношении нескольких допустимых аудиторий. Любые идеи будут оценены.
Ответ №1:
Решение было прямо передо мной :).
Метод входа в систему в MyProj.Идентификатор (который генерирует токен) должен включать утверждения аутентификации для каждой из аудиторий следующим образом:
namespace MyProj.Identity.Controllers
{
[ApiController]
[Route("api/authentication")]
public class AuthenticationController : ControllerBase
{
// ...
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginDto model)
{
// ...
foreach (var audience in _configuration.GetSection("JWT:ValidAudiences").Get<string[]>())
{
authClaims.Add(new Claim(JwtRegisteredClaimNames.Aud, audience));
}
// ...
var token = new JwtSecurityToken(
issuer: _configuration["JWT:ValidIssuer"],
expires: DateTime.Now.AddMinutes(int.Parse(_configuration["JWT:TokenExpiryMinutes"])),
claims: authClaims,
signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256));
}
// ...
}
}
Это решает проблему.