【问题标题】:Using token-based together with cookie-based authentication without email在不使用电子邮件的情况下使用基于令牌和基于 cookie 的身份验证
【发布时间】:2019-12-18 12:36:42
【问题描述】:

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

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 来控制不同角色对我的网络应用程序中不同操作的访问。

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

但是,我有两个问题:

  • 这确实是为此类场景生成身份验证令牌的最简单方法吗?
  • 到目前为止,我看到的所有 JWT 示例都包含对用户电子邮件的引用,例如生成令牌时将new Claim(JwtRegisteredClaimNames.Sub, user.Email) 添加到声明列表中。我根本不收集用户的电子邮件,这是我想保留的深思熟虑的决定。有什么方法可以使用例如代替用户名?

提前致谢!

【问题讨论】:

  • JWT 的子令牌就是任何用户标识符。如果您的用户名是唯一的,您可以使用它们,或者使用他们的数据库主键,或者为此目的为您的数据库中的每个用户提供一个随机 GUID。

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


【解决方案1】:

这确实是为这种情况生成身份验证令牌的最简单方法吗?

我假设这是参考问题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 类来创建令牌。我不知道这怎么能更简单。

有什么方法可以使用例如代替用户名?

索赔是您想要的任何东西。 System.IdentityModel.Tokens.Jwt Namespace 的内置声明名称很少,您可以从 JwtRegisteredClaimNames Struct 使用。

所以你可以使用以下之一:

JwtRegisteredClaimNames.NameId
JwtRegisteredClaimNames.Sid
JwtRegisteredClaimNames.UniqueName

或者您可以创建自己的。

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

更新 1

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

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,这是一个非常有用的建议!
  • 我添加了一些额外的信息会很有帮助。
  • 我接受你的回答,因为它完美地回答了我的问题,即使最后我采取了不同的方式(我将在我的解决方案中添加另一个答案)。谢谢!
【解决方案2】:

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

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

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

【讨论】:

  • JWT 与 cookie 一样安全。并不是说我试图改变你的想法,只是让其他阅读本文的人都能理解同样的事情。 (虽然从技术上讲它们不是一回事,因为您可以使用 cookie 来存储 JWT。)
  • 是的,这就是重点——因为我将令牌存储在 LS 中(而且 LS 不如 cookie 安全)——我不应该使用 JWT 或 cookie,而应该使用令牌尽可能少的功能和访问权限。
  • 另外,如果你打算使用存储,如果可以的话,我强烈推荐会话存储而不是本地存储。
  • 不幸的是我不能,但是解释为什么会太具体和太长。再次感谢您的帮助和 cmets!
猜你喜欢
  • 2021-02-08
  • 2016-10-01
  • 2016-11-02
  • 2015-10-29
  • 2017-12-28
  • 2020-11-16
  • 2015-12-24
  • 1970-01-01
  • 2016-02-21
相关资源
最近更新 更多