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

查看:16
本文介绍了.Net Core 2.0 Web API using 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.

我的问题是我不太清楚如何添加身份和在不破坏身份验证的情况下注册所有各种服务(角色管理器、用户管理器等)——基本上只要我将此行添加到我的 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)

推荐答案

我最终把解决方案放在一起,所以根据用户 alwayslearning 的建议,我编辑了我的帖子,并将其作为实际答案.

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<TUser>() 而不是services.AddIdentity<TUser>().然而,这并没有为角色管理添加一大堆东西,并且显然缺乏适当的构造函数来为其提供您想要使用的角色类型.这意味着就我而言,我必须这样做:

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 方法 CheckPasswordSignInAsyncnot 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 using JWT - 添加身份会破坏 JWT 身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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