使用自定义函数.NET Core 2

时间:2018-06-08 08:28:03

标签: asp.net-core jwt bearer-token

在我的API项目中,我使用JwtBearer处理身份验证(用户使用Azure登录)。调用API时,使用定义的Azure实例验证令牌,这一切都正常。

成功验证令牌后,登录用户将插入具有适当角色的我们自己的数据库中。现在处理的方式如下:

// Add authentication (Azure AD)
            services
                .AddAuthentication(sharedOptions =>
                {
                    sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                    sharedOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 
                    sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 
                })
                .AddJwtBearer(options =>
                {
                    options.Audience = this.Configuration["AzureAd:ClientId"];
                    options.Authority = $"{this.Configuration["AzureAd:Instance"]}{this.Configuration["AzureAd:TenantId"]}";

                    options.Events = new JwtBearerEvents()
                    {
                        OnTokenValidated = context =>
                        {
                            // Check if the user has an OID claim
                            if (!context.Principal.HasClaim(c => c.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier"))
                            {
                                context.Fail($"The claim 'oid' is not present in the token.");
                            }

                            ClaimsPrincipal userPrincipal = context.Principal;

                            // Check is user exists, if not then insert the user in our own database
                            CheckUser cu = new CheckUser(
                                context.HttpContext.RequestServices.GetRequiredService<DBContext>(),
                                context.HttpContext.RequestServices.GetRequiredService<UserManager<ApplicationUser>>(),
                                userPrincipal);

                            cu.CreateUser();

                            return Task.CompletedTask;
                        },
                    };
                });

这工作正常,但它不是最美丽/正确的方法。我会说我应该使用依赖注入/覆盖OnTokenValidated事件并集成&#39; CheckUser&#39;那里的逻辑使startup类保持整洁。

遗憾的是我缺乏关于DI的知识,我不完全确定最好的方法是正确处理这个问题。因此,我看了一下,找到了一个确切描述我的问题的帖子:

Problems handling OnTokenValidated with a delegate assigned in startup.cs

阅读完这篇文章之后,我尝试用自己的逻辑修改它,我最终得到了以下内容:

在初创公司:

        services.AddScoped<UserValidation>();

        services
            .AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                sharedOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 
            })
            .AddJwtBearer(options =>
            {
                options.Audience = this.Configuration["AzureAd:ClientId"];
                options.Authority = $"{this.Configuration["AzureAd:Instance"]}{this.Configuration["AzureAd:TenantId"]}";

                options.EventsType = typeof(UserValidation);
            });

自定义JwtBearerEvents类:

public class UserValidation : JwtBearerEvents
    {
        private string UserID { get; set; }

        private string UserEmail { get; set; }

        private string UserName { get; set; }

        public override async Task TokenValidated(TokenValidatedContext context)
        {
            try
            {
                TRSContext context2 = context.HttpContext.RequestServices.GetRequiredService<TRSContext>();
                UserManager<ApplicationUser> userManager = context.HttpContext.RequestServices.GetRequiredService<UserManager<ApplicationUser>>();

                ClaimsPrincipal userPrincipal = context.Principal;

                this.UserID = userPrincipal.Claims.First(c => c.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier").Value;

                if (userPrincipal.HasClaim(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"))
                {
                    this.UserEmail = userPrincipal.Claims.First(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress").Value;
                }

                if (userPrincipal.HasClaim(c => c.Type == "name"))
                {
                    this.UserName = userPrincipal.Claims.First(c => c.Type == "name").Value;
                }

                var checkUser = userManager.FindByIdAsync(this.UserID).Result;
                if (checkUser == null)
                {
                    checkUser = new ApplicationUser
                    {
                        Id = this.UserID,
                        Email = this.UserEmail,
                        UserName = this.UserEmail,
                    };

                    var result = userManager.CreateAsync(checkUser).Result;

                    // Assign Roles
                    if (result.Succeeded)
                    {
                        return;  
                    }
                    else
                    {
                        throw new Exception(result.Errors.First().Description);
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
        }
    }

然而,由于某种原因,这不起作用。没有错误,UserValidation永远不会被命中(尝试设置调试点,但它从不命中),并且它不会插入新用户(使用旧代码时也是如此)。

任何人都知道我在这里做错了什么或者有一些更好的想法如何处理这个?

1 个答案:

答案 0 :(得分:0)

我建议您在启动时进行基本的令牌验证(如权限和受众),如您所示。我建议您使用基于策略的验证来进行特定的声明验证。见Policy-based authorization in ASP.NET Core

结果将是更简单、更易于维护的代码。