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

查看:76
本文介绍了使用JWT的.Net Core 2.0 Web API-添加身份会破坏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内容不设置Authentication或使代码立即删除呢?我假设某个时候有些代码选择不运行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

EDIT - Found an answer
ok, I eventually found this GH issue which is basically exactly this problem: https://github.com/aspnet/Identity/issues/1376

基本上我需要做的是双重的:

Basically what I had to do was twofold:

确保首先

更改呼叫以从以下位置添加身份验证:

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>().但是,这并没有为角色管理添加很多东西,并且显然缺少适当的构造函数来为其提供要使用的Role类型.这意味着在我的情况下,我必须这样做:

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 not :

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.

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

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