.Net Core 2.0 Web API 使用 JWT - 添加身份会破坏 JWT 身份验证 [英] .Net Core 2.0 Web API using JWT - Adding Identity breaks the JWT authentication

查看:34
本文介绍了.Net Core 2.0 Web API 使用 JWT - 添加身份会破坏 JWT 身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(编辑 - 找到正确的修复!见下文)

好的 - 这是我第一次尝试 .Net Core 2.0 和身份验证,尽管我过去曾使用 Web API 2.0 做过一些事情,并且在过去几年中在各种 MVC 和 Webforms ASP 项目上进行了相当广泛的工作.

OK - this is my first attempt at .Net Core 2.0 and authentication, though I've done things with Web API 2.0 in the past, and have worked fairly extensively on various MVC and Webforms ASP projects over the last couple of years.

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

I'm trying to create a Web API ONLY project using .Net Core. This will form the back end of a multi-tenant application for producing some reports, so I need to be able to authenticate users. It seems the usual approach is to use JWT - first authenticate the user to generate a token, then pass that to the client to use on every API request. Data will be stored and retrieved using EF Core.

我关注了这篇文章以获得基本方法为了进行设置,我设法让它正常工作 - 我有一个控制器,它接受用户名/密码并在有效时返回令牌,并根据声明设置一些授权策略.

I followed this post for a basic way to get this set up, and I managed to get this to work ok - I have a controller which accepts a username/password and returns a token if valid, and some Authorization policies set up based on the claims.

接下来我需要的是实际管理用户/密码/等.我以为我只是为此使用 .Net Core Identity,因为这样我会有很多现成的代码来担心用户/角色、密码等.我使用的是自定义 User 类和 UserRole 类派生自标准 IdentityUserIdentityRole 类,但我现在已经恢复到标准类.

The next thing I need is to actually manage the users/passwords/etc. I thought I'd just use .Net Core Identity for this as that way I would have lots of ready-made code for worry about users/roles, passwords etc. I was using custom User class and UserRole classes which derived from the standard IdentityUser and IdentityRole classes, but I've since reverted to the standard ones now.

我遇到的问题是我不太清楚如何添加身份 &在不破坏身份验证的情况下注册所有各种服务(rolemanager、usermanager 等) - 基本上只要我将此行添加到我的 Startup.ConfigureServices 类:

The problem I have is that I can't quite figure out how to add identity & register all the various Services (rolemanager, usermanager, etc) without also breaking the authentication - basically as soon as I add this line to my Startup.ConfigureServices class:

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

一切都出错了,当我收到请求时,我再也看不到任何索赔,因此所有政策都被锁定,您无法获得任何信息.

It all goes wrong, and I can no longer see any claims when I receive a request, so all the policies just lock down and you can't get to anything.

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

If I don't have those lines, then I end up with errors related to UserManager, RoleManager, UserStore etc. all not being registered for DI.

那么...如何(如果可能)我如何注册身份并将其正确连接到上下文,但避免/删除对实际授权机制的任何更改?

So... how (if it's possible) can I register Identity and hook it up to the Context correctly, but avoid/Remove any changes to the actual Authorisation mechanism?

我在网上浏览了很多,但自 .Net Core 1.x 以来很多都发生了变化,所以很多教程等都不再有效.

I've looked around a fair bit online, but a lot of this has changed since .Net Core 1.x so a lot of the tutorials etc. aren't really valid any more.

我不打算让这个 API 应用程序有任何前端代码,所以我现在不需要任何表单或任何东西的 cookie 身份验证.

I'm not intending this API application to have any front-end code, so I don't need any cookie authentication for forms or anything for now.

编辑
好的,我现在发现在这段代码中,在 Startup.ConfigureServices() 方法中设置了 JWT 身份验证:

Edit
ok, I've now found that in this code setting up the JWT authentication in the Startup.ConfigureServices() method:

 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 已经设置了它...

If I put a breakpoint at the line indicated (via ">>breakpoint>>>") then it gets hit when I don't add the lines to add identity services, but if I do add those lines then it never gets hit. This is true no matter where in the method I put the services.AddIdentity() call. I get that this is simply a lambda so it's getting executed at a later point, but is there any way I can get the AddIdentity stuff to NOT set up Authentication, or make the code immediately remove it? I assume at some point there's some code which elects to not run the Lambda for config I've set there as the Identity stuff has already set it...

感谢您阅读所有内容:)

Thanks for reading all that if you have :)

编辑 - 找到答案
好的,我最终发现了这个 GH 问题,这基本上就是这个问题:https://github.com/aspnet/Identity/issues/1376

基本上我必须做的有两个:

Basically what I had to do was twofold:

确保对 services.AddIdentity 的调用首先

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

Change the call to add auth from:

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

致:

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

这确实令人讨厌地导致创建了一个 cookie,但据我所知,这并没有用于身份验证 - 它纯粹是在对具有 [Authorize(Policy)= "Administrator")] 或至少类似的设置.

This does annoyingly result in a cookie being created, but this isn't then used for authentication as far as I can tell - it's purely using the bearer token on requests to controllers/actions which have [Authorize(Policy = "Administrator")] or similar set at least.

我需要进行更多测试,如果我发现它以某种方式不起作用,我会尝试回到这里更新.

I need to test more, and I'll try to come back here an update this if I find it is not working in some way.

(已编辑 - 现在将正确的解决方案作为答案)

(Edited - put proper solution in as an answer now)

推荐答案

我最终整理了解决方案,因此根据用户始终学习的建议,我编辑了我的帖子,并将其作为实际答案.

I eventually put together the solution, so on the suggestion of user alwayslearning I've edited my post and I'm putting this in as an actual answer.

好的,这可以正确完成.首先,您需要使用我在上面的编辑中指出的身份验证选项 - 这很好.那么你需要使用services.AddIdentityCore() 而不是services.AddIdentity().然而,这并没有为角色管理添加一大堆东西,并且显然缺乏适当的构造函数来为其提供您想要使用的角色类型.这意味着就我而言,我必须这样做:

ok, This can be done properly. First, you need to use the Authentication options I pointed out in my edit above - that's fine. Then you need to useservices.AddIdentityCore<TUser>() rather than services.AddIdentity<TUser>(). This however, doesn't add a whole bunch of things for role management, and is apparently lacking the proper constructor to give it the type of Role you want to use. This means that in my case I had to do this:

  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:

Having done that, the next thing is to make sure that when validating a user login (prior to sending a token), you make sure to use the SignInManager method CheckPasswordSignInAsync and not 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;
    }

如果您使用 PasswordSignInAsync 方法,那么您将收到运行时错误.未配置 IAuthenticationSignInHandler.

if you use the PasswordSignInAsync method then you'll get a runtime error re. No IAuthenticationSignInHandler being configured.

我希望这能在某些时候对某人有所帮助.

I hope this helps someone at some point.

这篇关于.Net Core 2.0 Web API 使用 JWT - 添加身份会破坏 JWT 身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆