在没有电子邮件的情况下使用基于令牌和基于 cookie 的身份验证

Using token-based together with cookie-based authentication without email

我正在使用 Identity 开发 .NET Core MVC 项目。我目前有一个正常的基于 cookie 的身份验证的工作项目,摘自身份配置:

public class IdentityHostingStartup : IHostingStartup
{
    public void Configure(IWebHostBuilder builder)
    {
        builder.ConfigureServices((context, services) => {
            services.AddDbContext<TauManagerIdentityDbContext>(options =>
                options.UseNpgsql(
                    context.Configuration.GetConnectionString("TauManagerIdentityDbContextConnection")));

            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddRoles<IdentityRole>()
                .AddRoleManager<ApplicationRoleManager>()
                .AddEntityFrameworkStores<TauManagerIdentityDbContext>()
                .AddUserManager<ApplicationIdentityUserManager>()
                .AddDefaultUI()
                .AddDefaultTokenProviders();
        });
    }
}

我正在使用 AuthorizeAttribute 来控制不同角色对我的 Web 应用程序中不同操作的访问。

现在我面临的情况是,我必须只对一个特定的操作使用某种基于令牌的身份验证。我在 SO 上阅读了几篇关于使用 .NET Core Identity 设置 JWT 的文章和问题,我发现最接近我的案例的是

但是,我有两个问题:

提前致谢!

Is this indeed the simplest method of generating auth tokens for such scenario?

我假设这是参考问题中的代码 Using Identity with token and cookie authentication :

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

var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

var token = new JwtSecurityToken(_config["Tokens:Issuer"],
  _config["Tokens:Issuer"],
  claims,
  expires: DateTime.Now.AddMinutes(30),
  signingCredentials: creds);

创建 JWT 时,程序员必须提供的两条信息是声明和签名的创建方式。在前面的代码中,claims 作为您需要的声明,keycreds 是创建签名所必需的。要创建完整令牌,您可以使用 JwtSecurityToken class 来创建令牌。我不知道这怎么能更简单。

Is there any way to use e.g. username instead?

声明是您想要的任何内容。 System.IdentityModel.Tokens.Jwt Namespace has few built-in claim names you can use from JwtRegisteredClaimNames Struct.

因此您可以使用以下其中一项:

JwtRegisteredClaimNames.NameId
JwtRegisteredClaimNames.Sid
JwtRegisteredClaimNames.UniqueName

或者您可以创建自己的。

我认为潜在的问题是,如何确保我的 JWT 自动针对 ASP.Net Identity 进行授权。如果您使用 cookie,您应该能够查看 ClaimsPrinciple 并确定 Identity 使用哪些声明来验证请求并将这些相同的声明放入您的 JWT。

更新 1

我目前仅将 JWT 与 Angular 用于一个项目。我的代码不会完整,因为其中一些代码仅适用于 JWT/Bearer,但它应该有所帮助。我相信最重要的部分是 AddJwtBearer,如果我没弄错的话,它会寻找 bearer header valid/decode 并用 httpcontext.user 填充ClaimsPrincipal 以及所有相关声明。这些声明可以与 AuthorizeAttribute 一起使用以进行授权(声明 and/or 策略)。

StartUp.cs

    public void ConfigureServices(IServiceCollection services)
    {
        services
            .Configure<JwtIssuerOptions>(jwtIssuerOptionsConfig)
            .Configure<JwtIssuerOptions>(options =>
            {
                options.SigningCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha512);
            });

        services
            .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.IncludeErrorDetails = true;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ClockSkew = TimeSpan.FromMinutes(5),
                    IssuerSigningKey = symmetricSecurityKey,
                    RequireSignedTokens = true,
                    RequireExpirationTime = true,
                    ValidateLifetime = true,
                    ValidAudience = jwtIssuerOptions.Audience,
                    ValidateIssuer = true,
                    ValidIssuer = jwtIssuerOptions.Issuer
                };
                if (_isDevelopment)
                {
                    options.Events = new JwtBearerEvents
                    {
                        OnAuthenticationFailed = c =>
                        {
                            Debug.WriteLine(c.Exception.Message);
                            return Task.CompletedTask;
                        },
                    };
                }
            });

所以,最后我走了不同的路。在我的例子中,我觉得 JWT 有点矫枉过正,因为我打算将生成的令牌存储在浏览器的本地存储中(安全性低)——因此我决定根本不使用 JWT。如果有人可以拦截 JWT 令牌,他们在技术上至少获得了用户的 一些 访问权限,我需要很好地微调 JWT 内部的权限以使其安全。

所以,我刚刚实现了一个生成随机字节串(使用 System.Security.Cryptography.RandomNumberGenerator)并将其存储为 IdentityUser 对象的属性的操作。

我还用 [AllowAnonymous] 修饰了我想公开给基于令牌的访问的操作,并在那里添加了我自己的验证例程(基本上检查当前 ControllerBase.User 是否具有适当的权限或传递的令牌参数具有属于有权调用相应操作的用户之一的值)。我意识到这为一些额外的安全风险打开了大门(例如此端点上的潜在 DDOS,如果我不打开它会部分缓解),但这是我愿意采取的权衡。