代码之家  ›  专栏  ›  技术社区  ›  GPW

.Net Core 2.0 Web API使用JWT-添加标识会破坏JWT身份验证

  •  21
  • GPW  · 技术社区  · 7 年前

    好的,这是我第一次尝试。Net Core 2.0和身份验证,尽管我过去用过Web API 2.0,在过去几年中,我在各种MVC和Webforms ASP项目上做了相当广泛的工作。

    我正在尝试使用创建一个仅限Web API的项目。净核心。这将形成多租户应用程序的后端,用于生成一些报告,因此我需要能够对用户进行身份验证。通常的方法似乎是使用JWT-首先对用户进行身份验证以生成令牌,然后将其传递给客户端以在每个API请求中使用。将使用EF Core存储和检索数据。

    我跟着 this post 为了实现这一设置的基本方法,我设法使其正常工作——我有一个控制器,它接受用户名/密码并在有效时返回令牌,以及一些基于声明的授权策略。

    User 类别和 UserRole 源自标准的类别 IdentityUser IdentityRole 课程,但我现在又回到了标准课程。

    Startup.ConfigureServices 类别:

    services.AddIdentity<IdentityUser, IdentityRole>()
        .AddEntityFrameworkStores<MyContext>();
    

    如果我没有这些行,那么最终会出现与UserManager、RoleManager、UserStore等相关的错误。所有这些都没有注册到DI。

    我在网上看了很多东西,但从那以后很多事情都变了。Net Core 1。所以很多教程等都不再有效了。

    编辑
    好的,我现在发现在这段代码中,在 Startup.ConfigureServices() 方法:

     services.AddAuthentication(
                JwtBearerDefaults.AuthenticationScheme)
                    .AddJwtBearer(options =>
                    {
                     >>breakpoint>>>   options.TokenValidationParameters =
                            new TokenValidationParameters
                            {
                                ValidateIssuer = true,
                                ValidateAudience = true,
                                ValidateLifetime = true,
                                ValidateIssuerSigningKey = true,
    
                                ValidIssuer = "Blah.Blah.Bearer",
                                ValidAudience = "Blah.Blah.Bearer",
                                IssuerSigningKey =
                                JwtSecurityKey.Create("verylongsecretkey")
    
                            };
                    });
    

    如果我在指示的行上放置断点(通过“>breakpoint>”)然后当我 不要 从不 被击中。这是真的,无论我把方法放在哪里 services.AddIdentity() 呼叫我知道这只是一个lambda,所以它会在稍后执行,但是有什么方法可以让额外的东西不设置身份验证,或者让代码立即删除它?我假设在某个时刻,有一些代码选择不运行我在那里设置的Lambda for config,因为标识已经设置好了。。。

    编辑-找到答案
    https://github.com/aspnet/Identity/issues/1376

    基本上我需要做两件事:

    确保呼叫 services.AddIdentity<IdentityUser, IdentityContext() 已制作

    将调用更改为从以下位置添加身份验证:

    services.AddAuthentication(
                JwtBearerDefaults.AuthenticationScheme)
                    .AddJwtBearer(options =>
    ...
    

    收件人:

    services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
                .AddJwtBearer(options =>
    ...
    

    这确实会导致创建一个cookie,但据我所知,它并没有用于身份验证——它只是在向控制器/操作发出请求时使用承载令牌,而这些控制器/操作 [Authorize(Policy = "Administrator")] 或至少类似的集合。

    我需要进行更多测试,如果我发现它在某种程度上不起作用,我会尝试回来更新。

    (已编辑-立即输入适当的解决方案作为答案)

    2 回复  |  直到 7 年前
        1
  •  26
  •   GPW    7 年前

    我最终提出了解决方案,因此根据用户alwayslearning的建议,我编辑了我的帖子,并将其作为实际答案。

    好的,这可以做得很好。首先,您需要使用我在上面的编辑中指出的身份验证选项-这很好。 services.AddIdentityCore<TUser>() 而不是 services.AddIdentity<TUser>()

      IdentityBuilder builder = services.AddIdentityCore<IdentityUser>(opt =>
            {
                opt.Password.RequireDigit = true;
                opt.Password.RequiredLength = 8;
                opt.Password.RequireNonAlphanumeric = false;
                opt.Password.RequireUppercase = true;
                opt.Password.RequireLowercase = true;
            }
            );
            builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole), builder.Services);
            builder
                .AddEntityFrameworkStores<MyContext>();
            //.AddDefaultTokenProviders();
    
            builder.AddRoleValidator<RoleValidator<IdentityRole>>();
            builder.AddRoleManager<RoleManager<IdentityRole>>();
            builder.AddSignInManager<SignInManager<IdentityUser>>();
    

    完成后,下一件事是确保在验证用户登录时(在发送令牌之前),确保使用SignInManager方法 CheckPasswordSignInAsync PasswordSignInAsync

    public async Task<IdentityUser> GetUserForLogin(string userName, string password)
        {   
            //find user first...
            var user = await _userManager.FindByNameAsync(userName);
    
            if (user == null)
            {
                return null;
            }
    
            //validate password...
            var signInResult = await _signInManager.CheckPasswordSignInAsync(user, password, false);
    
            //if password was ok, return this user.
            if (signInResult.Succeeded)
            {
                return user;
            }
    
            return null;
        }
    

    如果您使用 密码符号同步 方法,然后您将得到运行时错误re。未配置IAuthenticationSignInHandler。

        2
  •  4
  •   Marek Sebera    3 年前

    AddIdentity 来自github的代码,并在此基础上创建了一个扩展方法,该方法没有添加默认的Cookie验证器,现在与内置的非常相似 AddIdentityCore 但可以接受 IdentityRole

     /// <summary>
     /// Contains extension methods to <see cref="IServiceCollection"/> for configuring identity services.
     /// </summary>
     public static class IdentityServiceExtensions
     {
         /// <summary>
         /// Adds the default identity system configuration for the specified User and Role types. (Without Authentication Scheme)
         /// </summary>
         /// <typeparam name="TUser">The type representing a User in the system.</typeparam>
         /// <typeparam name="TRole">The type representing a Role in the system.</typeparam>
         /// <param name="services">The services available in the application.</param>
         /// <returns>An <see cref="IdentityBuilder"/> for creating and configuring the identity system.</returns>
         public static IdentityBuilder AddIdentityWithoutAuthenticator<TUser, TRole>(this IServiceCollection services)
             where TUser : class
             where TRole : class
             => services.AddIdentityWithoutAuthenticator<TUser, TRole>(setupAction: null);
    
         /// <summary>
         /// Adds and configures the identity system for the specified User and Role types. (Without Authentication Scheme)
         /// </summary>
         /// <typeparam name="TUser">The type representing a User in the system.</typeparam>
         /// <typeparam name="TRole">The type representing a Role in the system.</typeparam>
         /// <param name="services">The services available in the application.</param>
         /// <param name="setupAction">An action to configure the <see cref="IdentityOptions"/>.</param>
         /// <returns>An <see cref="IdentityBuilder"/> for creating and configuring the identity system.</returns>
         public static IdentityBuilder AddIdentityWithoutAuthenticator<TUser, TRole>(this IServiceCollection services, Action<IdentityOptions> setupAction)
             where TUser : class
             where TRole : class
         {
             // Hosting doesn't add IHttpContextAccessor by default
             services.AddHttpContextAccessor();
             // Identity services
             services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
             services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
             services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
             services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
             services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
             // No interface for the error describer so we can add errors without rev'ing the interface
             services.TryAddScoped<IdentityErrorDescriber>();
             services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
             services.TryAddScoped<ITwoFactorSecurityStampValidator, TwoFactorSecurityStampValidator<TUser>>();
             services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
             services.TryAddScoped<UserManager<TUser>>();
             services.TryAddScoped<SignInManager<TUser>>();
             services.TryAddScoped<RoleManager<TRole>>();
    
             if (setupAction != null)
             {
                 services.Configure(setupAction);
             }
    
             return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
         }
     }
    

    现在,您可以从这样的WebApi项目中正常使用上述代码

    .AddIdentityWithoutAuthenticator<User, IdentityRole>()