如何手动验证 JWT Asp.Net Core?

Posted

技术标签:

【中文标题】如何手动验证 JWT Asp.Net Core?【英文标题】:How Do I Manually Validate a JWT Asp.Net Core? 【发布时间】:2017-12-02 04:34:48 【问题描述】:

那里有数以百万计的指南,但似乎没有一个能满足我的需求。我正在创建一个身份验证服务器,它只需要发布和验证/重新发布令牌。所以我不能创建一个中间件类来“验证”cookie 或标头。我只是收到字符串的 POST,我需要以这种方式验证令牌,而不是 .net 核心提供的 Authorize 中间件。

我的初创公司包含我可以开始工作的唯一令牌发行者示例。

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseExceptionHandler("/Home/Error");

            app.UseStaticFiles();
            var secretKey = "mysupersecret_secretkey!123";
            var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));

            var options = new TokenProviderOptions
            

                // The signing key must match!
                Audience = "AllApplications",
                SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),

                Issuer = "Authentication"
            ;
            app.UseMiddleware<TokenProviderMiddleware>(Microsoft.Extensions.Options.Options.Create(options));

我可以在创建时使用中间件,因为我只需要截取用户名和密码的正文。中间件从前面的Startup.cs 代码中获取选项,检查请求路径并从下面看到的上下文中生成令牌。

private async Task GenerateToken(HttpContext context)

    CredentialUser usr = new CredentialUser();

    using (var bodyReader = new StreamReader(context.Request.Body))
    
        string body = await bodyReader.ReadToEndAsync();
        usr = JsonConvert.DeserializeObject<CredentialUser>(body);
    

    ///get user from Credentials put it in user variable. If null send bad request

    var now = DateTime.UtcNow;

    // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
    // You can add other claims here, if you want:
    var claims = new Claim[]
    
        new Claim(JwtRegisteredClaimNames.Sub, JsonConvert.SerializeObject(user)),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim(JwtRegisteredClaimNames.Iat, now.ToString(), ClaimValueTypes.Integer64)
    ;

    // Create the JWT and write it to a string
    var jwt = new JwtSecurityToken(
        issuer: _options.Issuer,
        audience: _options.Audience,
        claims: claims,
        notBefore: now,
        expires: now.Add(_options.Expiration),
        signingCredentials: _options.SigningCredentials);
    var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

    ///fill response with jwt

上面的这个大块代码将DeserializeCredentialUser json ,然后执行一个返回用户对象的存储过程。然后,我将添加三个声明,然后将其寄回。

我能够成功生成 jwt,并使用 jwt.io 之类的在线工具,我输入了密钥,该工具说它是有效的,并带有一个我可以使用的对象

    
         "sub": " User_Object_Here ",
         "jti": "96914b3b-74e2-4a68-a248-989f7d126bb1",
         "iat": "6/28/2017 4:48:15 PM",
         "nbf": 1498668495,
         "exp": 1498668795,
         "iss": "Authentication",
         "aud": "AllApplications"
    

我遇到的问题是了解如何根据签名手动检查声明。因为这是一个发布和验证令牌的服务器。像大多数指南一样,设置Authorize 中间件不是一种选择。下面我正在尝试验证令牌。

[Route("api/[controller]")]
public class ValidateController : Controller


    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Validate(string token)
    
        var validationParameters = new TokenProviderOptions()
        
            Audience = "AllMyApplications",
            SigningCredentials = new 
            SigningCredentials("mysupersecret_secretkey!123", 
            SecurityAlgorithms.HmacSha256),

            Issuer = "Authentication"
        ;
        var decodedJwt = new JwtSecurityTokenHandler().ReadJwtToken(token);
        var valid = new JwtSecurityTokenHandler().ValidateToken(token, //The problem is here
        /// I need to be able to pass in the .net TokenValidParameters, even though
        /// I have a unique jwt that is TokenProviderOptions. I also don't know how to get my user object out of my claims
    

【问题讨论】:

【参考方案1】:

stole主要从 ASP.Net Core 源代码中借用了这段代码:https://github.com/aspnet/Security/blob/dev/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs#L45

根据该代码我创建了这个函数:

private string Authenticate(string token) 
    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    List<Exception> validationFailures = null;
    SecurityToken validatedToken;
    var validator = new JwtSecurityTokenHandler();

    // These need to match the values used to generate the token
    TokenValidationParameters validationParameters = new TokenValidationParameters();
    validationParameters.ValidIssuer = "http://localhost:5000";
    validationParameters.ValidAudience = "http://localhost:5000";
    validationParameters.IssuerSigningKey = key;
    validationParameters.ValidateIssuerSigningKey = true;
    validationParameters.ValidateAudience = true;

    if (validator.CanReadToken(token))
    
        ClaimsPrincipal principal;
        try
        
            // This line throws if invalid
            principal = validator.ValidateToken(token, validationParameters, out validatedToken);

            // If we got here then the token is valid
            if (principal.HasClaim(c => c.Type == ClaimTypes.Email))
            
                return principal.Claims.Where(c => c.Type == ClaimTypes.Email).First().Value;
            
        
        catch (Exception e)
        
            _logger.LogError(null, e);
        
    

    return String.Empty;

validationParameters 需要与您的 GenerateToken 函数中的匹配,然后它应该可以正常验证。

【讨论】:

key 究竟来自哪里? @WWpana key 是函数的第一行。 SymmetricSeucirytKey 中的参数可以是任何你喜欢的string,只要确保它很长 请原谅我可能太天真了,但为什么要验证电子邮件声明而不是名称或名称标识符?电子邮件声明是否自动映射到令牌的主题? @pseabury 您实际上并没有验证电子邮件,而是在验证包含大量数据的整个令牌,就像您所说的那样。一旦你说“是的,这个令牌是有效的”,那么你就可以信任里面的数据了。对我来说,电子邮件地址是用户的主键,然后我用它来查询有关用户的其他信息。我在生成原始令牌时添加了电子邮件,因为我知道每个请求都需要它。 @JimWallace - 明白了。您可能还想查看 validator.CanReadToken(...) 上的 If/else。如果验证器无法读取令牌,我认为您会想抛出令牌无效的情况。

以上是关于如何手动验证 JWT Asp.Net Core?的主要内容,如果未能解决你的问题,请参考以下文章

JWT 身份验证 ASP.NET Core MVC 应用程序

如何使用 Microsoft 身份平台身份验证在 ASP.NET Core Web 应用程序中获取 JWT 令牌?

如何在 ASP.NET Core 5 中使用 AuthenticationBuild.AddJwtBearer() 执行 JWT 颁发者验证时添加参数

ASP.NET Core 中的 Jwt 令牌身份验证

ASP.NET Core JWT 身份验证受众属性

ASP.NET Core JWT 身份验证更改声明(子)