Цель такая: написать бэкенд ASP.Net Core MVC* SPA для работы с ReactJS и дальнейшей возможностью переиспользовать существующий API для создания, скажем, Android приложения.
Платформа: .Net Core 2.1
* - Если такое ещё можно назвать MVC, учитывая, что View не будет, а будет отдельная директория ClientApp со всем содержимым фронтэнда.
Пошуршав интернеты, наткнулся на то, что ASP.Net Core Identity не актуален. Звучит логично, учитывая, что тот сильно опирается на куки, а при общении через API куки таскать неудобно. Хотя многие примеры нижеупомянутого JWT всё же используют IdentityUser
Много инструкций с использованием JWT. Приличная часть из них слишком зациклена на фронтенд реализации и практически ничего не говорит о бэкенде. Не нашёл примеров с OAuth2, везде свой велосипед, причём Demo и не пригодный для реального использования.
В тех же примерах по JWT используются Issuer, Audience и SecretKey, но ни слова о том, по каким правилам их надо выбирать и/или генерировать, ну и где безопасно хранить (если исключить примеры с хардкодом, то их обычно хранили в appsettings.json).
Также в процессе гугления (конкретно: попытке найти инфу об JwtSecurityTokenHandler из System.IdentityModel.Tokens) MSDN Microsoft выдаёт:
We’re no longer updating this content regularly. Check the Microsoft Product Lifecycle for information about how this product, service, or technology is supported.
Что это значит? Microsoft более не поддерживает JWT? Технология в принципе уже неактуальна? Что же тогда использовать?
В итоге мне теперь не совсем понятно как строить авторизацию в своём приложении:
Нужные ли мне IdentityUser из Microsoft.AspNetCore.Identity?
Нужен ли мне IdentityDbContext из Microsoft.AspNetCore.Identity.EntityFrameworkCore?
Актуален ли JWT?
Как построить авторизацию с OAuth2?
Хотелось бы сохранить доступность авторизованного пользователя из HttpContext.User и к его Claims, чтобы не мучать БД лишний раз для получения Id/UserName/Avatar/LastOnline/etc.
А также учесть то, что активно будут использоваться роли пользователей.
UPD: Немного обновлю конечные цели, чтобы стало понятнее:
Это форум с разделами, постами и комментариями в формате вопрос-ответ
Реализация фронтенда через SPA
Планируется открытое API
Это же API будет использовать SPA
Поддержка быстрой регистрации/входа через сторонние сервисы (Vk, FB, Google, etc.)
Ответ
Вот проверенный код, который работает.
Итак, настройка аутентификации в Startup
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
services.AddDbContext
services.AddIdentity
services.Configure
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
// укзывает, будет ли валидироваться издатель при валидации токена
ValidateIssuer = false,
// будет ли валидироваться потребитель токена
ValidateAudience = false,
// будет ли валидироваться время существования
ValidateLifetime = true,
// установка ключа безопасности
IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(),
// валидация ключа безопасности
ValidateIssuerSigningKey = true,
};
});
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
});
// .........
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// ...............
app.UseAuthentication();
// ................
}
}
Класс AuthOptions
public class AuthOptions
{
const string KEY = "mysupersecret_secretkey!"; // ключ для шифрации
public const int LIFETIME = 300; // время жизни токена - 5 часов
public static SymmetricSecurityKey GetSymmetricSecurityKey()
{
return new SymmetricSecurityKey(Encoding.ASCII.GetBytes(KEY));
}
}
Моделька для запроса токена
public class TokenRequest
{
public string UserName { get; set; }
public string Password { get; set; }
}
Контроллер для получения токена
[Route("api/[controller]")]
public class AccountController : Controller
{
private readonly UserManager
public AccountController(UserManager
[HttpPost("[action]")]
public async Task
var principal = await GetPrincipal(username, password);
if (principal == null)
{
return StatusCode(400, "Invalid username or password.");
}
var now = DateTime.UtcNow;
// создаем JWT-токен
var jwt = new JwtSecurityToken(
notBefore: now,
claims: principal.Claims,
expires: now.Add(TimeSpan.FromMinutes(AuthOptions.LIFETIME)),
signingCredentials: new SigningCredentials(AuthOptions.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256));
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new
{
token = encodedJwt,
username = principal.Identity.Name
};
return Json(response);
}
private async Task
И, по моему, это все. С этим все остальные вещи типа атрибутов авторизации, роли и прочее работает из коробки. Насколько я помню, ничего дополнительно делать не надо.
Комментариев нет:
Отправить комментарий