【问题标题】:Where to store JWT Token in .net core web api?在 .net 核心 web api 中存储 JWT 令牌的位置?
【发布时间】:2019-04-26 14:41:37
【问题描述】:

我正在使用 web api 来访问数据,并且我想对 web api 进行身份验证和授权。为此,我正在使用 JWT 令牌身份验证。但我不知道应该在哪里存储访问令牌?

我想做什么?

1)登录后存储令牌

2)如果用户想访问web api的任何方法,检查token对该用户是否有效,如果有效则授予访问权限。

我知道两种方法

1) 使用 cookie

2)sql server 数据库

从上面存储令牌的更好方法是哪一种?

【问题讨论】:

  • 您能否进一步解释您的用例。您是否想在您的 webapi 上强制执行身份验证,以便您的调用者在调用您的控制器和操作之前需要进行身份验证,或者您的 webapi 是否正在调用另一个需要对您的调用进行身份验证的系统?如果您正在尝试实现先前的选项,则无需担心如果您已完成自定义中间件,除非您正在考虑拆分应用程序的多个实例并且您需要共享会话(不知道为什么要使用会议虽然)
  • 但是,如果您询问如何在成功验证后将令牌交付给调用者,您有两种选择。使用标准 owin 中间件使用承载来处理身份验证,然后您可以在响应中返回 jwt(风险在于您的最终客户端以保护令牌)或配置中间件以使用 cookie 身份验证并仅返回带有标头 Set 的 cookie cookie 然后您的客户端可以保存 cookie 而不必担心任何其他应用程序能够访问其信息
  • 你认为为什么需要将 jwt 存储在任何地方?
  • 通过比较token来验证用户是否有效

标签: api authentication jwt


【解决方案1】:

我不熟悉将用户令牌存储在后端应用程序中,我将快速检查它是如何工作的不需要在你身边存放任何东西。

如果您使用 cookie,那么您只需要配置中间件来验证 cookie 的有效性,如果它出现在用户/消费者的标头中,如果不可用或已过期或无法解析,您只需拒绝请求和用户甚至不会点击任何受保护的控制器和操作。这是一个非常简化的 cookie 方法。(我仍在使用它进行开发,尚未在生产中进行测试,但现在使用 JS 客户端和 Postman 在本地工作得很好)

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
       .AddCookie(options =>
       {
           options.Cookie.Name = "yourCookieName";
           options.Cookie.SameSite = SameSiteMode.None;//its recommended but you can set it to any of the other 3 depending on your reqirements
           options.Events = new Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationEvents
           {
               OnRedirectToLogin = redirectContext =>//this will be called if an unauthorized connection comes and you can do something similar to this or more
               {
                   redirectContext.HttpContext.Response.StatusCode = 401;
                   return Task.CompletedTask;
               },
               OnValidatePrincipal = context => //if a call comes with a valid cookie, you can use this to do validations. in there you have access to the request and http context so you should have enough to work with
               {
                   var userPrincipal = context.Principal;//I'm not doing anything with this right now but I could for instance validate if the user has the right privileges like claims etc
                   return Task.CompletedTask;
               }
           };
       });

显然这将被放置或调用在您启动的 ConfigureServices 方法中以注册身份验证

然后在 Startup 的 Configure 方法中,您可以像这样连接身份验证

app.UseAuthentication();

之前

app.UseMvc()

【讨论】:

    【解决方案2】:

    或者,如果您只是想使用 JWT 进行身份验证,则实现会略有不同

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(options =>
    {
        options.Events = new JwtBearerEvents
        {
            OnTokenValidated = context =>
            {
                var user = context.Principal.Identity.Name;
                //Grab the http context user and validate the things you need to
                //if you are not satisfied with the validation fail the request using the below commented code
                //context.Fail("Unauthorized");
                
                //otherwise succeed the request
                return Task.CompletedTask;
            }
        };
        options.RequireHttpsMetadata = false;
        options.SaveToken = true;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey("MyVeryStrongKeyHiddenFromAnyone"),
            ValidateIssuer = false,
            ValidateAudience = false
    
        };
    });
    

    在使用 MVC 之前仍然应用使用身份验证。

    [请注意,这些是非常简化的示例,您可能需要进一步加强安全性并实施最佳实践,例如使用强密钥、可能从环境中加载配置等]

    那么实际的身份验证操作,比如在 AuthenticationController 中可能类似于

    [Route("api/[controller]")]
    [Authorize]
    public class AuthenticationController : Controller
    {
        [HttpPost("authenticate")]
        [AllowAnonymous]
        public async Task<IActionResult> AuthenticateAsync([FromBody]LoginRequest loginRequest)
        {
            //LoginRequest may have any number of fields expected .i.e. username and password
    
            //validate user credentials and if they fail return
            //return Unauthorized();
    
            var claimsIdentity = new ClaimsIdentity(new Claim[]
               {
                //add relevant user claims if any
               }, "Cookies");
    
            var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
            await Request.HttpContext.SignInAsync("Cookies", claimsPrincipal);
            return Ok();
        }
    }
    

    在这种情况下,我使用 cookie,因此我使用 Set Cookie 返回 HTTP 结果。如果我使用的是 JWT,我会返回类似

    [HttpPost("authenticate")]
    public IActionResult Authenticate([FromBody]LoginRequest loginRequest)
    {
        //validate user credentials and if they validation failed return a similar response to below
        //return NotFound();
    
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes("MySecurelyInjectedAsymKey");
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new Claim[]
            {
                //add my users claims etc
            }),
            Expires = DateTime.UtcNow.AddDays(1),//configure your token lifespan and needed
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey("MyVerySecureSecreteKey"), SecurityAlgorithms.HmacSha256Signature),
            Issuer = "YourOrganizationOrUniqueKey",
            IssuedAt = DateTime.UtcNow
        };
    
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var tokenString = tokenHandler.WriteToken(token);
        var cookieOptions = new CookieOptions();
        cookieOptions.Expires = DateTimeOffset.UtcNow.AddHours(4);//you can set this to a suitable timeframe for your situation 
        cookieOptions.Domain = Request.Host.Value;
        cookieOptions.Path = "/";
        Response.Cookies.Append("jwt", tokenString, cookieOptions);
        return Ok();
    }
    

    【讨论】:

    • 感谢您的解决方案,但这里的 IUserDomain 是什么...?
    • 没问题,不用担心,这些基本上只是服务编排,有助于执行某些操作,例如访问数据库以验证用户凭据或调用内部身份验证系统。但是您是否掌握了不需要使用数据库来存储令牌的基本知识
    猜你喜欢
    • 2019-10-25
    • 2021-12-21
    • 2020-08-24
    • 1970-01-01
    • 2018-12-07
    • 2017-01-20
    • 2020-08-09
    • 2020-07-24
    • 1970-01-01
    相关资源
    最近更新 更多