.Net Core 2.0 Web API using JWT - 添加身份会破坏 JWT 身份验证
Posted
技术标签:
【中文标题】.Net Core 2.0 Web API using JWT - 添加身份会破坏 JWT 身份验证【英文标题】:.Net Core 2.0 Web API using JWT - Adding Identity breaks the JWT authentication 【发布时间】:2018-03-01 14:53:32 【问题描述】:(编辑 - 找到正确的修复方法!见下文)
好的 - 这是我第一次尝试 .Net Core 2.0 和身份验证,尽管我过去曾使用 Web API 2.0 做过一些事情,并且在过去几年中在各种 MVC 和 Webforms ASP 项目上进行了相当广泛的工作.
我正在尝试使用 .Net Core 创建一个仅限 Web API 的项目。这将形成用于生成一些报告的多租户应用程序的后端,因此我需要能够对用户进行身份验证。似乎通常的方法是使用 JWT - 首先对用户进行身份验证以生成令牌,然后将其传递给客户端以在每个 API 请求上使用。将使用 EF Core 存储和检索数据。
我遵循this post 的基本方法来进行此设置,并且我设法让它正常工作 - 我有一个控制器,它接受用户名/密码并在有效时返回令牌,并设置了一些授权策略根据声明。
接下来我需要实际管理用户/密码/等。我以为我会为此使用.Net Core Identity,因为这样我就有很多现成的代码来担心用户/角色、密码等。我使用的是自定义的User
类和UserRole
派生的类来自标准的 IdentityUser
和 IdentityRole
类,但我现在已经恢复到标准类。
我遇到的问题是,我不太清楚如何添加身份和注册所有各种服务(rolemanager、usermanager 等)而不破坏身份验证 - 基本上只要我将此行添加到我的 @ 987654327@班级:
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<MyContext>();
一切都出错了,当我收到请求时,我再也看不到任何索赔,所以所有政策都被锁定了,你什么也得不到。
如果我没有这些行,那么我最终会遇到与 UserManager、RoleManager、UserStore 等相关的错误。所有这些都没有为 DI 注册。
那么......我如何(如果可能的话)注册身份并将其正确连接到上下文,但避免/删除对实际授权机制的任何更改?
我在网上查了很多资料,但是自从 .Net Core 1.x 以来,很多情况都发生了变化,所以很多教程等都不再有效了。
我不打算让这个 API 应用程序有任何前端代码,所以我现在不需要对表单或任何东西进行任何 cookie 身份验证。
编辑
好的,我现在发现在这段代码中,在 Startup.ConfigureServices()
方法中设置 JWT 身份验证:
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,所以它会在稍后执行,但是有什么方法可以让 AddIdentity 的东西不设置身份验证,或者让代码立即删除它?我假设在某些时候有一些代码选择不运行我在那里设置的配置的 Lambda,因为 Identity 的东西已经设置了它......
感谢您阅读所有内容,如果您有 :)
编辑 - 找到答案 好的,我最终发现了这个 GH 问题,基本上就是这个问题: 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")]
或类似设置的控制器/操作的请求中使用不记名令牌最少。
我需要进行更多测试,如果我发现它在某些方面不起作用,我会尝试回到这里更新。
(已编辑 - 现在将正确的解决方案作为答案)
【问题讨论】:
你救了我。谢谢 你应该考虑把它分解成一个问题,然后自己作为一个正确的答案来回答。 @alwayslearning 不是一个坏主意...所以我这样做了。 【参考方案1】:我最终将解决方案放在一起,因此根据用户 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
和 不 @987654325 @:
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;
如果您使用PasswordSignInAsync
方法,那么您将收到运行时错误。没有配置 IAuthenticationSignInHandler。
我希望这对某些人有所帮助。
【讨论】:
你绝对帮了我。非常感谢。当您没有 JWT 或 Identity 经验时,互联网上有许多过时的教程,很难找到解决方案。 我想问一下builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole), builder.Services);
是做什么的?
@t123yh 这是解决方法的一部分,因为 AddIdentityCore() 方法没有一个构造函数,该构造函数采用正在使用的角色类型(在我的情况下,我只是使用内置的IdentityRole
类,而从它派生以向角色添加自定义属性是很常见的,在这种情况下,您需要将派生类传递给它)。基本上,它是启用后续行的解决方法,这些行将 RoleValidator、RoleManager 和 SignInManager 添加/注册到 DI 系统,以便所有中间件都可以使用它们。
很好的答案感谢您还提到了 checkpassworkasync 调用。不知道为什么这些信息似乎被埋没了,而且很难在 .net core 中找到 auth 的例子。
@GPW 尽管我遇到的所有帖子都使用了重新实例化方法,但它似乎无需重新实例化 IdentityBuilder 变量就可以正常工作。 var builder = services.AddIdentityCore(o => ... ) .AddRoles我从 github 中提取了AddIdentity
代码,并基于它创建了一个扩展方法,它不添加默认的 Cookie Authenticator,它现在与内置的 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>()
【讨论】:
+1 表示努力。我可能会使用它,但我现在所拥有的一切正常。对于让生活更轻松绝对有用,希望这个问题的任何新手现在都会有更整洁的代码:)以上是关于.Net Core 2.0 Web API using JWT - 添加身份会破坏 JWT 身份验证的主要内容,如果未能解决你的问题,请参考以下文章
ASP.NET Core 2.0 中 Web API 的本地用户帐户存储
asp.net core 2.0 web api基于JWT自定义策略授权
.Net Core 2.0 Web API 使用 Identity / JWT 并让用户管理器使用 DI
用VSCode开发一个asp.net core2.0+angular5项目: Angular5+asp.net core 2.0 web api文件上传
.Net Core 2.0 Web API using JWT - 添加身份会破坏 JWT 身份验证
将 JWT Bearer Authentication Web API 与 Asp.Net Core 2.0 结合使用的问题