Простая аутентификация/авторизация на токенах в ядре asp.net для хранилища Mongodb

Мне нужно реализовать довольно простой автомат с двумя основными функциями: Owners и Users. И я думаю, что Enum для этого будет достаточно. Само приложение - это SPA с webapi, реализованным через ядро ​​Asp.net. Я видел статью - как ее реализовать с помощью EF Identity, но их модели выглядят намного сложнее, чем я на самом деле, и EF ориентированы на SQL db, и я использую mongo. Поэтому мой пользователь будет выглядеть примерно так:

class UserModel{
    Id, 
    Token, 
    Roles: ["Owners", "Users"],
    ...
}

Итак, какие интерфейсы мне нужно реализовать и добавить в DI, чтобы иметь возможность использовать [Authorize] и [Authorize(Roles="Users")], и они работали правильно на основе токена, который я отправляю в заголовке?

Ответ 1

Позвольте мне пояснить небольшой ответ @Adem. Вам необходимо реализовать специализированное промежуточное ПО определенным образом. Существует три абстрактных класса, которые должны быть реализованы для реализации этого (ответ верный для asp.net core rc2 btw):

Microsoft.AspNetCore.Builder.AuthenticationOptions  Microsoft.AspNetCore.Authentication.AuthenticationMiddleware<TOptions>  Microsoft.AspNetCore.Authentication.AuthenticationHandler<TOptions>

а затем добавьте это промежуточное программное обеспечение в ваш класс запуска.

Пример кода:

public class TokenOptions : AuthenticationOptions
    {
        public TokenOptions() : base()
        {
            AuthenticationScheme = "Bearer";
            AutomaticAuthenticate = true;
        }
    }

public class AuthMiddleware : AuthenticationMiddleware<TokenOptions>
{
    protected override AuthenticationHandler<TokenOptions> CreateHandler()
    {
       return new AuthHandler(new TokenService());
    }

    public AuthMiddleware(RequestDelegate next, IOptions<TokenOptions> options, ILoggerFactory loggerFactory, UrlEncoder encoder) : base(next, options, loggerFactory, encoder)
    {
    }
}

public class AuthHandler : AuthenticationHandler<TokenOptions>
{
    private ITokenService _tokenService;

    public AuthHandler(ITokenService tokenService)
    {
        _tokenService = tokenService;
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        string token = null;
        AuthenticateResult result = null;
        string token = Helper.GetTokenFromHEader(Request.Headers["Authorization"]);
        // If no token found, no further work possible
        if (string.IsNullOrEmpty(token))
        {
            result = AuthenticateResult.Skip();
        }
        else
        {
            bool isValid = await _tokenService.IsValidAsync(token);
            if (isValid)
            {
                //assigning fake identity, just for illustration
                ClaimsIdentity claimsIdentity = new ClaimsIdentity("Custom");
                var claims = new List<Claim>();
                claims.Add(new Claim(ClaimTypes.Name, "admin"));
                claims.Add(new Claim(ClaimTypes.NameIdentifier, "admin"));
                claims.Add(new Claim(ClaimTypes.Role, "admin"));
                ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
                result =
                    AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal,
                        new AuthenticationProperties(), Options.AuthenticationScheme));
            }
            else
            {
                result = AuthenticateResult.Skip();
            }
        }

        return result;
    }
}`

p.s. Код предназначен только для иллюстрации идеи. Конечно, вам нужно будет реализовать свой собственный обработчик.

Ответ 2

Вы можете использовать пользовательский middleware для аутентификации пользователя и установить claims (имя, роли и т.д.).

Я попытаюсь написать простой middleware:

Сначала создайте middlware class:

public class CustomMiddleware
{
    private readonly RequestDelegate _next;
    private readonly UserRepository _userRepository;

    public CustomMiddleware(RequestDelegate next, UserRepository userRepository)
    {
        _next = next;
        _userRepository = userRepository; 
    }

    public async Task Invoke(HttpContext context)
    {
        string token = context.Request.Headers["Token"];
        var user = _userRepository.Get(token);
        ClaimsIdentity claimsIdentity = new ClaimsIdentity("Custom");
        var claims = new List<Claim>();
        claims.Add(new Claim(ClaimTypes.Name, "admin"));
        claims.Add(new Claim(ClaimTypes.NameIdentifier, "admin"));
        foreach(var role in user.Roles)
        {
            claims.Add(ClaimTypes.Role, role);
        }
        ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
        context.User = claimsPrincipal;
        await _next(context);
    }
}

Затем используйте middleware в Startup.cs следующим образом:

   public void Configure(IApplicationBuilder app)
    {
        app.UseMiddleware<CustomMiddleware>();
        ...
    }

Наконец, используйте атрибут Authorize:

[Authorize(Roles = "Users")]
public IActionResult Index()
{
}