结合使用基于令牌和基于cookie的身份验证而不使用电子邮件

时间:2019-08-11 18:36:19

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

我正在使用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来控制不同角色对Web应用程序中不同动作的访问。

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

但是,我有两个问题:

  • 这确实是针对这种情况生成身份验证令牌的最简单方法吗?
  • 到目前为止,我看到的所有JWT示例都包含对用户电子邮件的引用,例如在生成令牌时,将new Claim(JwtRegisteredClaimNames.Sub, user.Email)添加到声明列表中。我完全不收集用户的电子邮件,这是我要保留的故意决定。有没有办法使用例如而是用户名?

谢谢!

2 个答案:

答案 0 :(得分: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标头,并用httpcontext.user填充ClaimsPrincipal所有相关索赔。这些声明可以与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;
                        },
                    };
                }
            });

答案 1 :(得分:0)

所以,最后我走了不同的路。在我的情况下,我觉得JWT太过分了,因为我要将结果令牌存储在浏览器的本地存储中(安全性较低)-因此我决定根本不使用JWT。如果某人可以拦截JWT令牌,则从技术上讲,他们至少会获得用户的 some 访问权限,而我需要很好地微调JWT内部的权限,以使其安全。

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

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