Для реализации кастомного атрибута `[Authorize(Policy = "TwoFactorPolicy")]` в ASP.NET 8 и возврата различных сообщений об ошибках в зависимости от уровня авторизации, вам нужно следовать нескольким шагам. Я дам вам полный пример реализации, включая создание кастомного атрибута, обработчика, а также настройку политик.
### Шаг 1: Создание требования и обработчика
Для начала, создайте класс требования `TwoFactorAuthorizationRequirement` и обработчик, который будет его обрабатывать:
```csharp
public class TwoFactorAuthorizationRequirement : IAuthorizationRequirement
{
// Здесь можно определить дополнительные параметры требования, если это нужно
}
public class TwoFactorAuthorizationHandler : AuthorizationHandler<TwoFactorAuthorizationRequirement>
{
private readonly ITwoFactorAuthHelpService _twoFactorAuthService;
private readonly IHttpContextAccessor _httpContextAccessor;
public TwoFactorAuthorizationHandler(ITwoFactorAuthHelpService twoFactorAuthService, IHttpContextAccessor httpContextAccessor)
{
_twoFactorAuthService = twoFactorAuthService;
_httpContextAccessor = httpContextAccessor;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, TwoFactorAuthorizationRequirement requirement)
{
var user = context.User;
if (user == null || !user.Identity.IsAuthenticated)
{
// Пользователь не авторизован
context.Fail();
return;
}
// Предполагается, что у вас есть метод для проверки состояния двухфакторной аутентификации
bool isTwoFactorEnabled = await _twoFactorAuthService.IsTwoFactorEnabledAsync(user);
if (isTwoFactorEnabled)
{
// Проверяем, есть ли успешный двухфакторный код
if (await _twoFactorAuthService.IsTwoFactorCodeValidAsync(user))
{
context.Succeed(requirement);
}
else
{
// Если код не валиден, провалить с определённым сообщением
context.Fail(new AuthorizationFailureReason("Invalid two-factor authentication code."));
}
}
else
{
// Если двухфакторная аутентификация не включена
context.Succeed(requirement);
}
}
}
```
### Шаг 2: Регистрация требуемого обработчика в Startup.cs
В `Program.cs` (или `Startup.cs`, в зависимости от конфигурации) вам нужно зарегистрировать политику и обработчик:
```csharp
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("TwoFactorPolicy", policy =>
policy.Requirements.Add(new TwoFactorAuthorizationRequirement()));
});
builder.Services.AddSingleton<IAuthorizationHandler, TwoFactorAuthorizationHandler>();
```
### Шаг 3: Создание кастомного атрибута
Теперь создайте кастомный атрибут на основе стандартного `[Authorize]`:
```csharp
using Microsoft.AspNetCore.Authorization;
public class TwoFactorAuthorizeAttribute : AuthorizeAttribute, IAuthorizationPolicyProvider
{
public TwoFactorAuthorizeAttribute()
{
Policy = "TwoFactorPolicy";
}
}
```
### Шаг 4: Обработка ответов с ошибками
Теперь вам нужно скорректировать обработчик ошибок в контроллере, чтобы возвращать специфические сообщения. Это можно сделать в централизованном обработчике:
```csharp
public class CustomAuthorizationFailureHandler : IAsyncAuthorizationFilter
{
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (context.Result is ForbidResult)
{
// Убедитесь, что это именно ошибка двухфакторной аутентификации
// Вам нужно проверить причины неудачи в вашем обработчике
var reasons = (context.HttpContext.Items["AuthorizationFailureReasons"] as IEnumerable<AuthorizationFailureReason>)?.ToList();
if (reasons != null && reasons.Any())
{
var firstReason = reasons.First().Message; // Получаем первое сообщение
context.Result = new UnauthorizedObjectResult(new { message = firstReason });
}
}
else if (context.Result is UnauthorizedResult)
{
context.Result = new UnauthorizedObjectResult(new { message = "User is not authenticated." });
}
await Task.CompletedTask; // Можно сделать дополнительные асинхронные действия здесь, если нужно
}
}
```
### Шаг 5: Использование атрибута в контроллерах
Теперь вы можете использовать ваш кастомный атрибут в контроллерах:
```csharp
[ApiController]
[Route("[controller]")]
public class SampleController : ControllerBase
{
[HttpGet]
[TwoFactorAuthorize]
public IActionResult GetSecureData()
{
return Ok("This is secure data.");
}
}
```
### Зак