Как авторизовать пользователей по ролям в контроллерах MVC, используя токен JWT, полученный из вызова ajax - PullRequest
0 голосов
/ 28 мая 2018

Я работаю над одним проектом ASP.NET Core MVC, где я размещаю основной веб-сайт MVC, а также контроллер WebApi для обработки каждого вызова из представлений.

Я на самом деле не используювесь рабочий процесс MVC, только части маршрутизации и авторизации, представления написаны в виде простого javascript и html, поэтому в ASP.NET MVC нет таких элементов, как помощники по тегам или модели.

На самой первой странице индексаУ меня есть форма входа в систему, она вызывает (используя jquery) действие в контроллере WebApi, там я использую ASP.NET Identity Core для авторизации пользователя, если пользователь и пароль верны, я возвращаю токен JWT.

[Produces("application/json")]
[Route("api/[controller]")]
[AllowAnonymous]
[ApiController]
public class AccountController : ControllerBase
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly ILogger _logger;
    private readonly IConfiguration _configuration;

    public AccountController(
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> signInManager,
        ILogger<AccountController> logger,
        IConfiguration configuration)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _logger = logger;
        _configuration = configuration;
    }

    [Route("test")]
    [HttpGet]
    public async Task<IActionResult> Test()
    {
        return Ok(await Task.FromResult("Endpoint working"));
    }

    [Route("login")]
    [HttpPost]
    public async Task<IActionResult> Login([FromBody] LoginDto model)
    {
        var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, false, false);

        if (result.Succeeded)
        {
            var appUser = _userManager.Users.SingleOrDefault(r => r.Email == model.Email);

            return Ok(await GenerateJwtToken(model.Email, appUser));
        }

        return BadRequest(string.Empty);
    }

    [Route("logout")]
    [HttpGet]
    public async Task Logout()
    {
        await _signInManager.SignOutAsync();
    }

    private async Task<object> GenerateJwtToken(string email, IdentityUser user)
    {
        var claims = new List<Claim>
        {
            new Claim(JwtRegisteredClaimNames.Sub, email),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(ClaimTypes.NameIdentifier, user.Id)
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtKey"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        var expires = DateTime.Now.AddDays(Convert.ToDouble(_configuration["JwtExpireDays"]));

        var token = new JwtSecurityToken(
            _configuration["JwtIssuer"],
            _configuration["JwtIssuer"],
            claims,
            expires: expires,
            signingCredentials: creds
        );

        return await Task.FromResult(new JwtSecurityTokenHandler().WriteToken(token));
    }
}

Эта часть отлично работает, в базе данных у меня есть пара пользователей со своими ролями.Проблема в том, что после входа в систему я хочу перенаправить пользователя на контроллер MVC, в котором действия защищены ролью, контроллер выглядит так:

public class SecuredController : Controller
{
    [Authorize]
    public IActionResult Index()
    {
        return View();
    }

    [Authorize(Roles = "Role1, Admin")]
    public IActionResult Register()
    {
        return View();
    }

    [Authorize(Roles = "Role2, Admin")]
    public IActionResult Fraud()
    {
        return View();
    }
}

После входа я делаю что-то вроде

window.location = '@Url.Action("Index", "Secured")'

Но я всегда получаю ответ 401.

Можно ли добавить заголовок авторизации с токеном для перенаправления?Будет ли контроллер понимать роли пользователя из токена?

Буду признателен за ваше руководство

1 Ответ

0 голосов
/ 28 мая 2018

Хорошая новость заключается в том, что у вас даже есть несколько вариантов здесь.Я пишу это со своего мобильного телефона, поэтому оно будет очень кратким:

  1. Вы можете использовать ServiceFilterAttribute, чтобы указать CustomJwtAuthorizationFilter, который выполняет синтаксический анализ вашего JWT, извлекаяутверждает и материализует IPrincipal.Identity для привязки к User защите в вашем защищенном контроллере следующим образом:

    [ServiceFilter(typeof(CustomJwtAuthorizationFilter))]
    public class SecuredController : Controller
    {
        ...
    }
    
    public class CustomJwtAuthorizationFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext actionContext)
        {
            // get claims out of JWT in the request's Authorization header
            var claims = ...
            var identity = new ClaimsIdentity(claims);
            actionContext.HttpContext.User = new ClaimsPrincipal(identity);
            ...
        }
    }
    

    Хитрость заключается в том, чтобы убедиться, что утвержденияПредставленные роли четко различаются, если установить для их свойства ClaimType значение RoleClaimType, которое в большинстве случаев должно быть по умолчанию "role" !!!?.Но это то, что должно быть почти прозрачно сделано, поскольку утверждения ролей должны быть такими, как указано в JWT.

  2. Используйте некоторое существующее промежуточное ПО и настройте DI на вашем Startup.ConfigureServices(IServiceCollection).

...