索赔原则发现第一个价值不适用于服务附加性

Posted

技术标签:

【中文标题】索赔原则发现第一个价值不适用于服务附加性【英文标题】:Claims principle find first value not working with services addidentity 【发布时间】:2021-10-23 06:02:22 【问题描述】:

我正在使用 .NET 5 开发 Web API。我有一个端点可以获取当前用户

    [Authorize]
    [HttpGet]
    public async Task<ActionResult<UserDto>> GetCurrentUser()
    
        ApplicationUser user = await userManager.FindByEmailAsync(User.FindFirstValue(ClaimTypes.Email));

        return CreateUserObject(user);
    

现在,如果我像下面这样添加依赖项,这个方法就可以正常工作了。

services.AddIdentityCore<ApplicationUser>(opt =>
        
            //options go here
            opt.User.RequireUniqueEmail = true;
        )

但是,如果我使用 AddIdentity() 而不是 AddIdentityCore() 这个方法会失败

        // AddIdentity registers the same services as AddIdentityCore, with a few extras:
        // https://***.com/questions/55361533/addidentity-vs-addidentitycore
        services.AddIdentity<ApplicationUser, IdentityRole>(opt =>
        //services.AddIdentityCore<ApplicationUser>(opt =>
        
            //options go here
            opt.User.RequireUniqueEmail = true;
        )
        .AddEntityFrameworkStores<DataContext>()
        .AddSignInManager<SignInManager<ApplicationUser>>();


        SymmetricSecurityKey key = new(Encoding.UTF8.GetBytes(config["TokenKey"]));

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            
                options.TokenValidationParameters = new TokenValidationParameters
                
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = key,
                    ValidateIssuer = false,
                    ValidateAudience = false,
                ;
            );
        services.AddScoped<TokenService>();

“状态码”:500, "message": "值不能为空。(参数'email')", "details": " 在 Microsoft.AspNetCore.Identity.UserManager1.FindByEmailAsync(String email)\r\n at Spacestation.API.Controllers.AccountController.GetCurrentUser() in F:\\Projects\\Spacestation\\Spacestation.API\\Controllers\\AccountController.cs:line 86\r\n at lambda_method18(Closure , Object )\r\n at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\r\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeActionMethodAsync&gt;g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask1 actionResultValueTask)\r\n 在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker 调用程序,任务 lastTask,State next,Scope 范围,对象状态,Boolean isCompleted)\r\n 在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)\r\n 在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object&状态,Boolean& isCompleted)\r\n 在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()\r\n--- 从上一个位置结束堆栈跟踪 ---\r\n 在 Microsoft.AspNetCore.Mvc .Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker 调用者,Task lastTask,State next,Scope 范围,Object 状态,Boolean isCompleted)\r\n 在 Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker 调用者,Task任务, IDisposable 范围)\r\n 在 Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(端点终结点、任务 requestTask、ILogger 记录器)\r\n 在 Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext 上下文)\r\ n 在 Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)\r\n 在 Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)\r\n 在 Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext , ISwaggerProvider swaggerProvider)\r\n 在 F:\Projects\Spacestation\Spacestation.API\Middleware\ExceptionMiddleware.cs:line 28 中的 Spacestation.API.Middleware.ExceptionMiddleware.InvokeAsync(HttpContext context)"

编辑 #1

编辑 #2 - 令牌服务

public string CreateToken(ApplicationUser user)
    
        List<Claim> claims = new()
        
            new Claim(ClaimTypes.Name, user.UserName),
            new Claim(ClaimTypes.NameIdentifier, user.Id),
            new Claim(ClaimTypes.Email, user.Email),
        ;

        SymmetricSecurityKey key = new(Encoding.UTF8.GetBytes(config["TokenKey"]));
        SigningCredentials creds = new(key, SecurityAlgorithms.HmacSha512Signature);

        var tokenDescriptor = new SecurityTokenDescriptor
        
            Subject = new ClaimsIdentity(claims),
            Expires = DateTime.Now.AddDays(7),
            SigningCredentials = creds
        ;

        var tokenHandler = new JwtSecurityTokenHandler();

        var token = tokenHandler.CreateToken(tokenDescriptor);

        return tokenHandler.WriteToken(token);

    

【问题讨论】:

您能检查一下User 中是否有ClaimTypes.Email 吗?我们知道AddIdentity() 包含AddIdentityCore() 的实现,就像this answer 说的那样。而且我还找到了similar question here 而且我也不知道您在使用 AddIdentityCore() 时如何收到电子邮件。您是否将电子邮件设置为自定义属性?这是我的调试截图:i.stack.imgur.com/V97XA.png 我参考了这个教程来生成我的测试项目,这是我的配置。i.stack.imgur.com/j1Tc0.png 所以当使用AddIdentityCore 时,我在用户中有这些属性。但它们不在AddIdentity 中,请参阅编辑#1 你能在用户通过身份验证之前进行调试吗? 【参考方案1】:

我不确定这是否适合你,但我添加了一些配置和额外的 AddAuthentication:

        services.AddIdentity<IdentityUser, IdentityRole>(
        options => 
            options.SignIn.RequireConfirmedAccount = true;
            options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;
            options.Tokens.AuthenticatorTokenProvider = TokenOptions.DefaultEmailProvider;
            options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;
        )
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();
    services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
       .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, act => 
           act.LoginPath = "/identity/account/login";
           act.AccessDeniedPath = "/identity/account/accessdenied";
           act.SlidingExpiration = true;
       );

【讨论】:

我添加了这些配置,但仍然抛出同样的错误 我还添加了完整的身份验证配置【参考方案2】:

我新created一个net 5 mvc项目和Change Authentication to Individual User Accounts,我发现它默认使用services.AddDefaultIdentity,所以我查询它和services.AddIdentity之间的区别,结果是这样的

调用 AddDefaultIdentity 类似于调用以下内容:

添加身份

添加默认用户界面

AddDefaultTokenProviders

而且,这对我有用:

public void ConfigureServices(IServiceCollection services)
        
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));
            services.AddDatabaseDeveloperPageExceptionFilter();

            //services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
            //    .AddEntityFrameworkStores<ApplicationDbContext>();

            services.AddIdentity<IdentityUser, IdentityRole>(opt =>
            
                opt.User.RequireUniqueEmail = true;
            ).AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders().AddDefaultUI();
            services.AddControllersWithViews();
        

【讨论】:

我将AddDefaultTokenProviders() 方法添加到我的服务中,但是,我的User 中仍然没有设置任何声明。我也没有AddDefaultUI()。我不明白为什么在核心版本中设置了我的声明,但在使用 AddIdentity() 时却没有 我认为最好通过official document向github报告问题。

以上是关于索赔原则发现第一个价值不适用于服务附加性的主要内容,如果未能解决你的问题,请参考以下文章

ReactJS onClick 不适用于菜单中的第一个元素

Hook 不适用于第三个应用程序

附加 DOM 元素不适用于 jQuery 可选

4面技术5面HR附加笔试面,牛皮轰轰

来自画廊工作的文件选择器,但它不适用于 android webview 中的相机

附加不适用于唯一的呼叫 ID