使用HttpOnly Cookie的JWT授权

时间:2019-10-23 02:34:14

标签: c# authentication asp.net-core cookies jwt

我正在使用ASP.NET Core(使用CookieAuthentication作为我的MVC身份验证和用于Web-API的JWT)制作Web应用程序。

我已经配置了cookie身份验证并成功创建了JWT令牌。但是,我很难授权包含[Authorization(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]属性的API方法。

Startup.cs

services
    .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.Cookie.Name = "CookieAuthentication";
        options.Cookie.HttpOnly = true;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.ExpireTimeSpan = TimeSpan.FromDays(14);
        options.LoginPath = "/Account/Login";
        options.LogoutPath = "/Account/Logout";
        options.AccessDeniedPath = "/Account/Unauthorized";
    });

services
    .AddAuthentication()
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:JwtIssuer"],
            ValidAudience = Configuration["Jwt:JwtAudience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:JwtSecretKey"]))
        };
    });

AccountController.cs

[HttpPost]
[Route("Login")]
[AllowAnonymous]
public async Task<IActionResult> Login([FromForm]LoginViewModel model)
{
    await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

    if (model.Username == "test" && model.Password == "test")
    {
        var name = "Name";
        var role = "Admin";
        var email = "test123@gmail.com";

        var claims = new List<Claim>()
        {
            new Claim(ClaimTypes.Name, name),
            new Claim(ClaimTypes.Email, email),
            new Claim(ClaimTypes.Role, role)
        };

        var credientials = new SigningCredentials(
            new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(_config["Jwt:JwtSecretKey"])),
                SecurityAlgorithms.HmacSha256);

        var securityToken = new JwtSecurityToken(   
            issuer: _config["Jwt:JwtIssuer"],
            audience: _config["Jwt:JwtIssuer"],
            claims: claims,
            expires: DateTime.Now.AddMinutes(1),
            signingCredentials: credientials
        );

        HttpContext.Response.Cookies.Append(
            "JwtCookie",
            "Bearer " + new JwtSecurityTokenHandler().WriteToken(securityToken),
            new CookieOptions
            {
                Expires = DateTime.Now.AddMinutes(1),
                HttpOnly = true,
                Secure = true,
                SameSite = SameSiteMode.Strict
            });

            var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

            var authenticationProperties = new AuthenticationProperties()
            {
                ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(1),
                IsPersistent = true
            };

            await HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                new ClaimsPrincipal(claimsIdentity),
                authenticationProperties);

            #endregion

            return Redirect($"{role}/Index");

Private.cshtml

@{
    ViewData["Title"] = "Private";
}

<h2>Private Area</h2>

<input type="button" onclick="addUser()" value="Login" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
    function addUser()
    {
        $.ajax
            ({
                type: 'POST',
                url: '/api/Test/AddUser'
            }).done(function (data)
            {
                console.log("done");
            }).fail(function (data, textStatus, xhr)
            {
                console.log("fail");
            })
    }
</script>

/ api / Test / AddUser方法

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpPost]
[Route("AddUser")]
public IActionResult AddUser()
{
    var u = new User
    {
        SamAccountName = "Test",
        Role = ""
    };
    _context.Add(u);
    try
    {
        _context.SaveChanges();
    }
    catch (Exception e)
    {
        return BadRequest($"Cannot save: {e}");
    }

    return Ok();
}

我的问题是:

  1. 当我尝试调用api/Test/AddUser方法时出现401错误。有没有一种方法可以自动通过授权标头发送包含JWT令牌的cookie,类似于CookieAuthentication的方法吗?我知道这是因为我没有Authorization标头。但是,因为我将JWT令牌存储在HttpOnly cookie中,所以无法找到将令牌传递到授权标头中的方法。我可以添加授权{“ Bearer” +令牌}。我将不得不使用sessionStorage或使用普通的cookie,但这会弹出一些安全问题。我看过许多教程,但从未见过javascript如何传递其授权标头,大多数显示了通过Postman中的授权标头传递。

  2. 如果可以完成上述操作,是否可以将过期日期与CookieAuthentication Cookie进行同步?由于无法像Cookie一样延长JWT令牌的到期日期,因此,如果它们使用两种不同类型的身份验证,该如何确保用户能够访问API和MVC?

  3. 该项目仅使用HTTPS,并且我在提交的表单中实现了防伪令牌。我应该防范任何攻击吗?

1 个答案:

答案 0 :(得分:0)

有点晚了,但我认为您会通过这个答案找到问题的答案

How can I validate a JWT passed via cookies?

基本思路是创建一个中间件来拦截请求,检查cookie中是否包含token,提取token,配置请求的授权头。在此之后,您的请求应该具有带有令牌的授权标头,并且应该启动 Authorize 数据注释