如何验证 JwtSecurityToken 自定义属性

Posted

技术标签:

【中文标题】如何验证 JwtSecurityToken 自定义属性【英文标题】:How to Validate JwtSecurityToken Custom Property 【发布时间】:2020-04-18 04:26:17 【问题描述】:

我正在尝试将自定义属性添加到在 ASP.NET Core Web API (3.1) 中生成的不记名令牌。我目前有一个运行良好的登录过程,它将发出一个用于授权请求的不记名令牌。我可以看到一个问题,如果有人破解用户名并通过,他们将拥有有效的 JWT 并可以访问我的 api。

我正在将此 API 用于移动应用 (flutter),并希望增加额外的安全性。使用颤振/飞镖,我可以访问一个唯一的设备 ID。我希望在不记名令牌中包含设备 ID,并根据请求从发出的 JWT 中提取设备 ID,并从 DEVICE_ID 请求标头值中对其进行验证,以确认它来自最初请求并生成 JWT 的同一设备.

我的第一个问题是我该怎么做?我找到了一篇关于 Payload (token.Payload["custom prop"] = ...) 的帖子 here,但我如何在授权期间提取它?有没有更好的不同方法来实现这一点?

我希望保留验证,这样我就可以简单地继续使用我的 api 控制器方法上方的 [Authorize] 属性。

这是我的启动 ConfigureServices 方法

 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                
                    options.TokenValidationParameters = new TokenValidationParameters
                    
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = Configuration["Jwt:Issuer"],
                        ValidAudience = Configuration["Jwt:Issuer"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])),

                    ;
                );

这里是我生成令牌的地方:

 private string GenerateJSONWebToken(UserModel user)
        
            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

            var claims = new[]
            
                new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                new Claim(JwtRegisteredClaimNames.Email, user.Email),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            ;

            var token = new JwtSecurityToken(
                issuer: _config["Jwt:Issuer"],
                audience: _config["Jwt:Issuer"],
                claims,
                expires: DateTime.Now.AddMinutes(120),
                signingCredentials: credentials);
            token.Payload["DEVICE_ID"] = 1001; // Added here just as example of Payload
            var encodeToken = new JwtSecurityTokenHandler().WriteToken(token);
            return encodeToken;
        

这里有一些只需要[Authorize]属性自动拒绝错误请求的地方方法:

        [Authorize]
        [HttpPost("Post")]
        public string Post()
        
            var identity = HttpContext.User.Identity as ClaimsIdentity;
            IList<Claim> claim = identity.Claims.ToList();
            var userName = claim[0].Value;
            return "Welcome To: " + userName;
        
        [Authorize]
        [HttpGet("GetValue")]
        public ActionResult<IEnumerable<string>> Get()
        
            return new string[]  "Value1", "Value2", "Value3" ;
        

我使用 this 在线教程为 API 构建了 JWT 授权。谢谢你的帮助。

我刚刚找到this (Microsoft: TokenValidationParameters.PropertyBag 并试图找到一些示例。在文档中它说“包含自定义键/值对的集合。这允许添加可用于自定义令牌验证场景的参数。”

【问题讨论】:

【参考方案1】:

您可以使用这样的负载来使用自定义声明:

var claims = new[]

    new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
    new Claim(JwtRegisteredClaimNames.Email, user.Email),
    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
    new Claim("device_id", "smaple_id")); // custom property claim
;

现在这里是一种简单的方法,而不是使用中间件创建自定义令牌验证器。

app.UseAuthentication();
app.Use(async (context, next) =>

    var deviceId = context.User.Claims.SingleOrDefault(x => x.Type == "device_id");
    var validationResult = DoSomeValidation(deviceId)
    if (validationResult == false)
    
        context.Response.StatusCode = 401;
        await context.Response.WriteAsync("Unauthorized Device");
    
    await next.Invoke();
);

要获得更简洁的代码,请在扩展方法中使用 from middleware。

【讨论】:

【参考方案2】:

Claims-Based authorization 是使用自定义声明的便捷方式。在您的 Startup.cs 中,您可以输入以下内容:

services.AddAuthorization(options =>

  options.AddPolicy("policy1", policy => policy.RequireClaim("CustomClaimName", "Accepted Value"));
);

然后在您的控制器中,您可以在要保护的方法中添加:

[Authorize(Policy = "policy1")]

【讨论】:

以上是关于如何验证 JwtSecurityToken 自定义属性的主要内容,如果未能解决你的问题,请参考以下文章

JwtSecurityToken 有最短过期时间吗?

无法从 JwtSecurityToken 获取签名

无法加载类型“System.IdentityModel.Tokens.JwtSecurityToken”

将 JwtSecurityToken 与 HttpClient 一起使用

JwtSecurityToken 返回错误的过期时间

JwtSecurityTokenHandler 返回小写声明类型