ASP.NET Core 2.0 中的多个身份

Posted

技术标签:

【中文标题】ASP.NET Core 2.0 中的多个身份【英文标题】:Multiple Identities in ASP.NET Core 2.0 【发布时间】:2018-05-06 02:34:40 【问题描述】:

我正在将 ASP.NET Core 1.0 应用程序迁移到 ASP.NET Core 2.0。

在我的启动中,我正在配置两个身份:

services.AddIdentity<IdentityUser, IdentityRole>(configureIdentity)
   .AddDefaultTokenProviders()
   .AddUserStore<IdentityUserStore<IdentityUser>>()
   .AddRoleStore<IdentityRoleStore<IdentityRole>>();

services.AddIdentity<Customer, CustomerRole>(configureIdentity)
   .AddDefaultTokenProviders()
   .AddErrorDescriber<CustomerIdentityErrorDescriber>()
   .AddUserStore<CustomerStore<Customer>>()
   .AddRoleStore<CustomerRoleStore<CustomerRole>>();

这在 ASP.NET Core 1.0 中运行良好,但因错误而失败:System.InvalidOperationException: 'Scheme already exists: Identity.Application' in ASP.NET Core 2.0。

在 ASP.NET Core 2.0 中,如果我删除对 AddIdentity 的调用之一,错误就会消失。如何迁移我的代码,以便我可以在我的应用程序中使用两种不同类型的身份用户和角色?还是当我在 ASP.NET Core 1.0 中编写此代码时,我只是在理解事情如何恢复时犯了一个根本性错误?

【问题讨论】:

【参考方案1】:

查看 github 上的 ASP.NET Core 源代码后,可以使用此扩展方法添加第二个身份:

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
using System.Collections.Generic;
using System.Text;

namespace Whatever

    public static class IdentityExtensions
    
        public static IdentityBuilder AddSecondIdentity<TUser, TRole>(
            this IServiceCollection services)
            where TUser : class
            where TRole : class
        
            services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
            services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
            services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
            services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
            services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
            services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
            services.TryAddScoped<UserManager<TUser>, AspNetUserManager<TUser>>();
            services.TryAddScoped<SignInManager<TUser>, SignInManager<TUser>>();
            services.TryAddScoped<RoleManager<TRole>, AspNetRoleManager<TRole>>();

            return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
        
    

【讨论】:

这真的很有帮助。但是为什么我们必须添加 Create a builder 方法; AddSecondIdentity? @ShiroiTora,我不确定我是否完全理解这个问题:您的意思是 是否有必要使用扩展方法? 还是您的意思是 为什么微软这样改设计? 我会回答第一个问题。在启动时,我使用services.AddIdentity&lt;..&gt; 作为我的第一个身份,然后使用services.AddSecondIdentity&lt;..&gt; 作为我的第二个身份。为什么我们需要这种扩展方法? @ShiroiTora,它不需要是扩展方法,但微软在 .NET Core 2.0 中改变了身份提供者的设计,使AddIdentity 不能用于添加更多身份。我认为他们的设计选择意味着应该使用单个身份,并且您应该在单个提供者中处理多个身份来源。我编写的扩展方法只是复制了 .NET Core 1.0 的行为方式,即它是从 .NET Core 的源代码中提取的。 我现在完全明白了。谢谢。【参考方案2】:

Asp.net Core 2.2 为此提供了一个内置方法。

AddIdentityCore<TUser>

使用方法:

services.AddIdentity<IdentityUser, IdentityRole>(configureIdentity)
   .AddDefaultTokenProviders()
   .AddUserStore<IdentityUserStore<IdentityUser>>()
   .AddRoleStore<IdentityRoleStore<IdentityRole>>();

services.AddIdentityCore<Customer>(configureIdentity)
   .AddDefaultTokenProviders()
   .AddErrorDescriber<CustomerIdentityErrorDescriber>()
   .AddUserStore<CustomerStore<Customer>>()
   .AddRoleStore<CustomerRoleStore<CustomerRole>>();

services.AddScoped<RoleManager<Customer>>();

其实这个方法的实现从asp.net core 2.2 github repo看

    /// <summary>
    /// Adds and configures the identity system for the specified User type. Role services are not added by default 
    /// but can be added with <see cref="IdentityBuilder.AddRolesTRole"/>.
    /// </summary>
    /// <typeparam name="TUser">The type representing a User in the system.</typeparam>
    /// <param name="services">The services available in the application.</param>
    /// <param name="setupAction">An action to configure the <see cref="IdentityOptions"/>.</param>
    /// <returns>An <see cref="IdentityBuilder"/> for creating and configuring the identity system.</returns>
    public static IdentityBuilder AddIdentityCore<TUser>(this IServiceCollection services, Action<IdentityOptions> setupAction)
        where TUser : class
    
        // Services identity depends on
        services.AddOptions().AddLogging();

        // Services used by identity
        services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
        services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
        services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
        services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
        services.TryAddScoped<IUserConfirmation<TUser>, DefaultUserConfirmation<TUser>>();
        // No interface for the error describer so we can add errors without rev'ing the interface
        services.TryAddScoped<IdentityErrorDescriber>();
        services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser>>();
        services.TryAddScoped<UserManager<TUser>>();

        if (setupAction != null)
        
            services.Configure(setupAction);
        

        return new IdentityBuilder(typeof(TUser), services);
    

【讨论】:

【参考方案3】:

非常感谢基思的回答。这为我节省了很多时间! 一个小的改进:在我的情况下,我必须配置一些选项(IdentityOptions)。例如:密码复杂性规则。

因此,我将操作 setupAction 的注册包括在内。 (这与 Microsoft 在 IdentityServiceCollectionExtension 中的 AddIdentity 中执行此操作的方式相同)

public static class IdentityExtensions

    public static IdentityBuilder AddSecondIdentity<TUser, TRole>(
        this IServiceCollection services, Action<IdentityOptions> setupAction)
        where TUser : class
        where TRole : class
    
        services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
        services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
        services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
        services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
        services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
        services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
        services.TryAddScoped<UserManager<TUser>, AspNetUserManager<TUser>>();
        services.TryAddScoped<SignInManager<TUser>, SignInManager<TUser>>();
        services.TryAddScoped<RoleManager<TRole>, AspNetRoleManager<TRole>>();

        if (setupAction != null)
            services.Configure(setupAction);

        return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
    

【讨论】:

我很困惑你们是如何使用它的?目前我有这个 services.AddDefaultIdentity() .AddEntityFrameworkStores(); services.AddSecondIdentity() .AddEntityFrameworkStores();但不知道如何初始化那些 2 来使用

以上是关于ASP.NET Core 2.0 中的多个身份的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 2.0 身份验证中间件

如何从 ASP.NET Core 2.0 中的自定义中间件请求身份验证

没有身份的 ASP.NET Core 2.0 承载身份验证

同一站点中的 asp net core 2.0 JWT 和 Openid Connect 身份验证

如何在 Asp.Net Core 2.0“AddJwtBearer”中间件中设置多个受众?

如何在 ASP.NET Core 2.0 中根据路由配置服务身份验证