Введение: зачем нужны OAuth 2.0 и JWT в современных приложениях?
В современной веб-разработке безопасность данных и управление доступом выходят на первый план. Каждый день миллионы пользователей входят в приложения через Google, Facebook или корпоративные системы, не передавая свои пароли напрямую. Именно здесь в игру вступают протоколы OAuth 2.0 и технология JWT (JSON Web Tokens).
OAuth 2.0 — это отраслевой стандарт для делегирования доступа. Он позволяет приложениям получать ограниченный доступ к ресурсам пользователя без раскрытия его учетных данных. JWT, в свою очередь, представляет собой компактный и самодостаточный формат токена, который содержит всю необходимую информацию для проверки подлинности и авторизации.
В этой статье мы на практике разберем, как реализовать OAuth 2.0 и JWT авторизацию на C# с использованием ASP.NET Core. Вы узнаете, как настроить сервер авторизации, сгенерировать токены и защитить свои API-эндпоинты. Все примеры кода готовы к использованию в реальных проектах.
1. Настройка сервера авторизации с IdentityServer4
Для реализации OAuth 2.0 мы воспользуемся популярной библиотекой IdentityServer4 — это фреймворк для ASP.NET Core, который реализует протоколы OpenID Connect и OAuth 2.0. Начнем с создания минимального сервера авторизации.
Шаг 1: Установка пакетов и создание проекта
Создайте новый проект ASP.NET Core Web Application (пустой шаблон) и установите необходимые NuGet-пакеты:
dotnet add package IdentityServer4
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Шаг 2: Конфигурация клиентов и ресурсов
Определите тестовых пользователей, клиентов и защищенные ресурсы. Создайте класс Config.cs:
public static class Config
{
public static IEnumerable<IdentityResource> IdentityResources =>
new[] { new IdentityResources.OpenId(), new IdentityResources.Profile() };
public static IEnumerable<ApiScope> ApiScopes =>
new[] { new ApiScope("api1", "My API") };
public static IEnumerable<Client> Clients =>
new[]
{
new Client
{
ClientId = "client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedScopes = { "api1" }
}
};
}
Шаг 3: Регистрация сервисов в Startup.cs
Настройте IdentityServer в методе ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients)
.AddTestUsers(TestUsers.Users);
services.AddAuthentication()
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:5001";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
});
}
В методе Configure добавьте вызов app.UseIdentityServer().
2. Реализация JWT токенов: генерация и валидация
JWT токен состоит из трех частей: заголовок (header), полезная нагрузка (payload) и подпись (signature). В .NET генерация и проверка JWT осуществляется с помощью встроенных механизмов.
Генерация JWT токена вручную
Иногда требуется создать токен вручную, например, для тестирования. Используйте следующий код:
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
using System.Text;
public string GenerateJwtToken(string userId, string role)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MySuperSecretKey123!"));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, userId),
new Claim(ClaimTypes.Role, role),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var token = new JwtSecurityToken(
issuer: "https://myapp.com",
audience: "https://myapp.com",
claims: claims,
expires: DateTime.UtcNow.AddHours(1),
signingCredentials: credentials
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
Валидация JWT токена
Проверка токена происходит автоматически при настройке JWT-аутентификации, но вы можете сделать это вручную:
public ClaimsPrincipal ValidateToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes("MySuperSecretKey123!");
var validationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = "https://myapp.com",
ValidAudience = "https://myapp.com",
IssuerSigningKey = new SymmetricSecurityKey(key)
};
return tokenHandler.ValidateToken(token, validationParameters, out _);
}
3. Защита API-эндпоинтов с помощью OAuth 2.0
Теперь, когда сервер авторизации настроен, защитим несколько API-эндпоинтов. Создадим контроллер, который требует валидный JWT токен.
Создание защищенного контроллера
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class DataController : ControllerBase
{
// Доступен только для авторизованных пользователей
[HttpGet]
public IActionResult GetProtectedData()
{
return Ok(new { Message = "Вы получили защищенные данные!" });
}
// Доступен только для пользователей с ролью Admin
[HttpGet("admin")]
[Authorize(Roles = "Admin")]
public IActionResult GetAdminData()
{
return Ok(new { Message = "Только для администраторов!" });
}
// Анонимный доступ
[AllowAnonymous]
[HttpGet("public")]
public IActionResult GetPublicData()
{
return Ok(new { Message = "Публичные данные" });
}
}
Получение токена и вызов API
Клиентское приложение должно сначала получить токен, а затем передавать его в заголовке Authorization:
// Получение токена через HTTP запрос
var client = new HttpClient();
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = "https://localhost:5001/connect/token",
ClientId = "client",
ClientSecret = "secret",