【问题标题】:How to get custom claims for IdentityServer4 Itself如何获取 IdentityServer4 本身的自定义声明
【发布时间】:2020-07-04 21:34:54
【问题描述】:

我正在运行托管在类似于https://github.com/IdentityServer/IdentityServer4/tree/master/samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity 的 MVC 应用程序中的 IdentityServer4。

这个 IdentityServer 主机在 ConfigureServices 方法的底部附近暴露了一个 ProfileService。

services.AddTransient<IProfileService, ProfileService>();

从我查看的所有示例和快速入门中,我没有看到 IdentityServer MVC 主机本身可以在哪里访问配置文件数据。这意味着 IDServ MVC 主机是其自身的客户端,并且可以访问声明数据。我已经看到了 IDServ 将 OpenIdConnect 添加为外部提供者的示例,但 MVC 应用程序似乎会将自己列为外部提供者,以便我可以获得 ProfileService 声明数据。

我的 Startup.cs(Host IDServ MVC App 的)看起来像这样

public class Startup
{
    public Startup(IConfiguration configuration, IHostingEnvironment env)
    {
        Configuration = configuration;
        HostingEnvironment = env;
    }

    ... removed for brevity


    public void ConfigureServices(IServiceCollection services)
    {

        var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name; 
        var connectionString = Configuration.GetConnectionString("connString");

        services.AddControllersWithViews();

        // configures IIS out-of-proc settings (see https://github.com/aspnet/AspNetCore/issues/14882)
        services.Configure<IISOptions>(iis =>
        {
            iis.AuthenticationDisplayName = "Windows";
            iis.AutomaticAuthentication = false;
        });

        // configures IIS in-proc settings
        services.Configure<IISServerOptions>(iis =>
        {
            iis.AuthenticationDisplayName = "Windows";
            iis.AutomaticAuthentication = false;
        });


        services.AddDbContext<AuthDbContext>(b =>
         b.UseSqlServer(connectionString,
             sqlOptions =>
             {
                 sqlOptions.MigrationsAssembly(typeof(AuthDbContext).GetTypeInfo().Assembly.GetName().Name);
                 sqlOptions.EnableRetryOnFailure(5, TimeSpan.FromSeconds(1), null);
             })
        ); 

        services.AddIdentity<ApplicationUser, IdentityRole>()
         .AddEntityFrameworkStores<AuthDbContext>()
         .AddDefaultTokenProviders();

        services.AddIdentityServer(options =>
        {
            options.Events.RaiseErrorEvents = true;
            options.Events.RaiseInformationEvents = true;
            options.Events.RaiseFailureEvents = true;
            options.Events.RaiseSuccessEvents = true;
        })
        .AddConfigurationStore(options =>
        {
            options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
        })
        .AddOperationalStore(options =>
        {
            options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
            options.EnableTokenCleanup = true;
        })
        .AddAspNetIdentity<ApplicationUser>()
        .AddSigningAuthority(HostingEnvironment, Configuration)
        .AddProfileService<ProfileService>();

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("Cookies");

        services.AddTransient<IProfileService, ProfileService>();

        services.AddTransient<AzureTableStorageLoggerMiddleware>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory, IHttpContextAccessor accessor)
    {
        app.UseMiddleware<AzureTableStorageLoggerMiddleware>();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }
        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();
        app.UseIdentityServer();
        app.UseAuthorization();

        loggerFactory.AddTableStorage(env.EnvironmentName + "Auth", Configuration["AzureStorageConnectionString"], accessor);
        app.UseMiddleware<AzureTableStorageLoggerMiddleware>(); 

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute(); 
        });
    }       

}

正如您在上面看到的,我没有在其自身上使用 .AddOpenIdConnect,我想知道是否需要在主机本身上添加它,以便我可以像这样在主机 IDServ 应用程序上获取配置文件服务声明数据...

services.AddAuthentication(options =>
{
   options.DefaultScheme = "Cookies";
   options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
   options.SignInScheme = "Cookies";
   options.Authority = "https://localhost:44378/"; //seems silly to have it point to it's own host
   options.RequireHttpsMetadata = false;
   options.GetClaimsFromUserInfoEndpoint = true;
   options.ClientId = "idserv";
   options.ClientSecret = "<<>>";
   options.ResponseType = "code id_token token";
   options.SaveTokens = true;
 });

从好的方面来说,一个完全独立的 MVC 客户端在使用 .AddOpenIdConnect() 中间件方法时确实会获取 ProfileService 声明数据,而不是主机。

谢谢

【问题讨论】:

    标签: asp.net-identity identityserver4


    【解决方案1】:
    1. 正如 IdentityServer 的文档所说:

    您可以提供回调以在验证后转换传入令牌的声明。要么使用辅助方法,例如:

        services.AddLocalApiAuthentication(principal =>
        {
            principal.Identities.First().AddClaim(new Claim("additional_claim", "additional_value"));
        
            return Task.FromResult(principal);
        });
    

    您可以在Claims Transformation阅读完整指南

    1. 您可以编写新的中间件并加载用户声明。
        public class ClaimsMiddleware
        {
            private readonly RequestDelegate _next;
    
            public ClaimsMiddleware(RequestDelegate next)
            {
                _next = next;
            }
    
            public async Task InvokeAsync(HttpContext httpContext, UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
            {
                if (httpContext.User != null && httpContext.User.Identity.IsAuthenticated)
                {
                    var sub = httpContext.User.Claims.SingleOrDefault(c => c.Type == JwtClaimTypes.Subject);
                    if (sub != null)
                    {
                        var user = await userManager.FindByIdAsync(sub.Value);
    
                        if (user != null)
                        {
                            var claims = //fill this variable in your way;
    
                            var appIdentity = new ClaimsIdentity(claims);
                            httpContext.User.AddIdentity(appIdentity);
                        }
                    }
    
                    await _next(httpContext);
                }
            }
        }
    

    并在您的 Startup.cs 中调用它

                app.UseIdentityServer();
                app.UseAuthorization();
    
                app.UseMiddleware<ClaimsMiddleware>();
    

    【讨论】:

    • 谢谢。有没有办法仅在 IDServ 主机 Startup.cs 文件中完成相同的结果?
    • 是的,有,您可以在添加 localApiAuthentication services.AddLocalApiAuthentication 时在 Startup.cs 文件中添加您的 calims。你可以在Claims Transformation找到完整的指南。
    • 中间件方法对我来说效果更好,这样我就可以通过 DI 访问 userManager。谢谢你梅尔达德
    猜你喜欢
    • 1970-01-01
    • 2019-02-09
    • 2021-09-12
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    • 1970-01-01
    • 2017-11-29
    • 1970-01-01
    相关资源
    最近更新 更多