【问题标题】:How to map IdentityServer4 Identity to any WebApp (.Net MVC Boilerplate, .Net Core Boilerplate)如何将 IdentityServer4 Identity 映射到任何 WebApp(.Net MVC Boilerplate、.Net Core Boilerplate)
【发布时间】:2019-09-09 17:27:57
【问题描述】:

我正在创建一个 SSO 服务器,将所有用户集中在 ActiveDirectory(AD) 中并在那里管理他们,而不是每个特定应用程序的数据库。

为了制作这个服务器,我使用了 IdentityServer4(Idsr4) 和 Ldap/AD Extension

我已将 Idsr4 设置为使用基于 AD 的身份(这是“中心化身份”),用户现在可以使用自己的 AD 登录名/密码登录 Idsr4

现在的问题是如何将中心化身份映射到应用程序。我想在多个应用程序中使用相同的身份用户。

我通读了 IdentityServer4 的文档,但找不到与提议的结构相关的任何内容。

有没有人有一个清晰的结构设置可以用来理解整个设置? (分离如 Asp.Net MVC Boilerplate、IdentityServer4、Protected Api。)

IdentityServer4 配置:

 public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        // configure identity server with in-memory stores, keys, clients and scopes
        services.AddIdentityServer()
            .AddDeveloperSigningCredential()
            ////.AddSigningCredential(...) // Strongly recommended, if you want something more secure than developer signing (Read The Manual since it's highly recommended)
            .AddInMemoryIdentityResources(InMemoryInitConfig.GetIdentityResources())
            .AddInMemoryApiResources(InMemoryInitConfig.GetApiResources())
            .AddInMemoryClients(InMemoryInitConfig.GetClients())
            .AddLdapUsers<OpenLdapAppUser>(Configuration.GetSection("IdentityServerLdap"), UserStore.InMemory);
    }

IdentityServer4 InMemoryInitConfig:

namespace QuickstartIdentityServer{
public class InMemoryInitConfig
{
    // scopes define the resources in your system
    public static IEnumerable<IdentityResource> GetIdentityResources()
    {
        return new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),
        };
    }

    public static IEnumerable<ApiResource> GetApiResources()
    {
        return new List<ApiResource>
        {
            new ApiResource("api1", "My API")
        };
    }

    // clients want to access resources (aka scopes)
    public static IEnumerable<Client> GetClients()
    {
        // client credentials client
        return new List<Client>
        {
            
            //DEMO HTTP CLIENT
            new Client
            {
                ClientId = "demo",
                ClientSecrets = new List<Secret> {new Secret("password".Sha256()) } ,
                ClientName = "demo",
                AllowedGrantTypes = {
                    GrantType.ClientCredentials, // Server to server
                    GrantType.ResourceOwnerPassword, // User to server
                    GrantType.Implicit
                },

                //GrantTypes.HybridAndClientCredentials,
                AllowAccessTokensViaBrowser = true,

                AllowOfflineAccess = true,
                AccessTokenLifetime = 90, // 1.5 minutes
                AbsoluteRefreshTokenLifetime = 0,
                RefreshTokenUsage = TokenUsage.OneTimeOnly,
                RefreshTokenExpiration = TokenExpiration.Sliding,
                UpdateAccessTokenClaimsOnRefresh = true,
                RequireConsent = false,

                RedirectUris = {
                    "http://localhost:6234/"
                },

                PostLogoutRedirectUris = { "http://localhost:6234" },
                AllowedCorsOrigins ={ "http://localhost:6234/" },

                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "api1"
                },
                
            },

            

        };
    }
}

}

我的客户端配置:

public void Configuration(IAppBuilder app)

    {
        
        app.UseAbp();

        app.UseOAuthBearerAuthentication(AccountController.OAuthBearerOptions);

        // ABP
        //app.UseCookieAuthentication(new CookieAuthenticationOptions
        //{
        //    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        //    LoginPath = new PathString("/Account/Login"),
        //    // evaluate for Persistent cookies (IsPermanent == true). Defaults to 14 days when not set.
        //    //ExpireTimeSpan = new TimeSpan(int.Parse(ConfigurationManager.AppSettings["AuthSession.ExpireTimeInDays.WhenPersistent"] ?? "14"), 0, 0, 0),
        //    //SlidingExpiration = bool.Parse(ConfigurationManager.AppSettings["AuthSession.SlidingExpirationEnabled"] ?? bool.FalseString)
        //    ExpireTimeSpan = TimeSpan.FromHours(12),
        //    SlidingExpiration = true
        //});
        // END ABP

        /// IDENTITYSERVER
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = "Cookies"
        });

        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
        {
            Authority = "http://localhost:5443", //ID Server
            ClientId = "demo",
            ClientSecret = "password",
            ResponseType = "id_token token",
            SignInAsAuthenticationType = "Cookies",
            RedirectUri = "http://localhost:6234/", //URL of website when cancel login on idsvr4
            PostLogoutRedirectUri = "http://localhost:6234", //URL Logout ??? << when this occor
            Scope = "openid",
            RequireHttpsMetadata = false,

            //AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,

        });
        /// END IDENTITYSERVER

        app.UseExternalSignInCookie("Cookies");
        
        app.MapSignalR();
    }

更新

我正在阅读 OpenID Connect 上的文档,发现可以为 httpContext 创建通知以在 Idsrv4 用户信息端点中获取用户声明,如下所示:

public void Configuration(IAppBuilder app)

    {
        
        app.UseAbp();

        // ABP
        //app.UseOAuthBearerAuthentication(AccountController.OAuthBearerOptions);

        //app.UseCookieAuthentication(new CookieAuthenticationOptions
        //{
        //    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        //    LoginPath = new PathString("/Account/Login"),
        //    // evaluate for Persistent cookies (IsPermanent == true). Defaults to 14 days when not set.
        //    //ExpireTimeSpan = new TimeSpan(int.Parse(ConfigurationManager.AppSettings["AuthSession.ExpireTimeInDays.WhenPersistent"] ?? "14"), 0, 0, 0),
        //    //SlidingExpiration = bool.Parse(ConfigurationManager.AppSettings["AuthSession.SlidingExpirationEnabled"] ?? bool.FalseString)
        //    ExpireTimeSpan = TimeSpan.FromHours(12),
        //    SlidingExpiration = true
        //});
        // END ABP

        /// IDENTITYSERVER
        AntiForgeryConfig.UniqueClaimTypeIdentifier = Thinktecture.IdentityModel.Client.JwtClaimTypes.Subject;
        JwtSecurityTokenHandler.DefaultInboundClaimFilter.Clear();

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = "Cookies"
        });

        // CONFIG OPENID
        var openIdConfig = new OpenIdConnectAuthenticationOptions
        {
            Authority = "http://localhost:5443", //ID Server
            ClientId = "demo",
            ClientSecret = "password",
            ResponseType = "id_token token",
            SignInAsAuthenticationType = "Cookies",
            RedirectUri = "http://localhost:6234/", //URL of website when cancel login on idsvr4
            PostLogoutRedirectUri = "http://localhost:6234", //URL Logout ??? << when this occor
            Scope = "openid profile api1",
            RequireHttpsMetadata = false,
            
            // get userinfo
            Notifications = new OpenIdConnectAuthenticationNotifications {
                SecurityTokenValidated = async n => {
                    var userInfoClient = new UserInfoClient(
                        new Uri(n.Options.Authority + "/connect/userinfo"),
                              n.ProtocolMessage.AccessToken);

                    var userInfo = await userInfoClient.GetAsync();
                    
                    // create new identity and set name and role claim type
                    var nid = new ClaimsIdentity(
                        n.AuthenticationTicket.Identity.AuthenticationType,
                        ClaimTypes.GivenName,
                        ClaimTypes.Role);

                    foreach (var x in userInfo.Claims) {
                        nid.AddClaim(new Claim(x.Item1, x.Item2));        
                    }

                    // keep the id_token for logout
                    nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));

                    // add access token for sample API
                    nid.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));

                    // keep track of access token expiration
                    nid.AddClaim(new Claim("expires_at", DateTimeOffset.Now.AddSeconds(int.Parse(n.ProtocolMessage.ExpiresIn)).ToString()));

                    // add some other app specific claim
                    //nid.AddClaim(new Claim("app_specific", "some data"));

                    n.AuthenticationTicket = new AuthenticationTicket(
                        nid,
                        n.AuthenticationTicket.Properties);

                    n.Request.Headers.SetValues("Authorization ", new string[] { "Bearer ", n.ProtocolMessage.AccessToken });

                }
            }

        };
        // END CONFIG OPENID

        app.UseOpenIdConnectAuthentication(openIdConfig);
        
        /// END IDENTITYSERVER

        app.UseExternalSignInCookie("Cookies");
        
        app.MapSignalR();
    }

更新 2

谢谢@Khanh TO,

我完全按照你的建议做了,我保留了每个应用程序的数据库

但是,为了不再通过应用程序数据库管理用户,我硬编码了一个从 idsr4 userinfo 端点获取的方法

在 abpUsers 表中创建或更新用户所需的信息,然后应用程序解释数据并执行必要的操作

更具体地说: 在我发送给客户的AccountControllerredirect_uri 中,我有一个ActionResult,它可以调用必要的方法在客户用户表上创建/更新用户

【问题讨论】:

  • 好的,读到这里,codeproject.com/Articles/1263539/… 这里有三个不同的身份验证 inc CookieAuthenticationOptions
  • @Gunblades 我不确定我是否完全理解这个问题。但我建议在每个应用程序中存储用户以及每个应用程序的specific 信息(但没有身份验证信息)。该结构类似于Users(Id, IdentityServerUserId,.....more specific information)IdentityServerUserId 是来自您的身份服务器的sub claim,您还可以在每个存储specific roles 的应用程序中拥有一个角色表。然后您可以在用户使用您的身份服务器登录后,使用IdentityServerUserId 映射到每个用户。
  • 关键是每个应用程序有不同的信息需求,我们将所有信息包括身份验证信息集中在您的身份服务器中,但我们为每个应用程序扩展了更具体的信息。
  • 我用 update 阅读了这篇文章,现在仍然很清楚你是否解决了问题,如果没有,问题是什么,@Gunblades
  • @Gunblades 问题中的问题解决了吗?

标签: c# asp.net-mvc identityserver4 aspnetboilerplate


【解决方案1】:

我认为GrantType.ResourceOwnerPassword流不支持AD登录UseOpenIdConnectAuthentication也不支持,你可以使用ImplicitHybrid流。
对客户端 mvc 应用程序进行身份验证后,您可以查看 HttpContext.User 中的任何声明并找到正确的声明值作为用户身份(它们只是声明,无需创建本地帐户)

【讨论】:

  • 我正在使用扩展名 (github.com/Nordes/IdentityServer4.LdapExtension),它允许我使用 GrantType.ResourceOwnerPassword 来执行此操作
  • @Gunblades 哦,我还没试过,但我认为UseOpenIdConnectAuthentication 本身也不支持密码流。密码流程要求您的应用程序接收用户名和密码并将其传递给您的身份验证中心 api(这意味着您的应用程序中必须有登录页面)。只需尝试implicthybired 流程,它非常易于使用(这些流程会将您的应用重定向到认证中心的登录页面并在您成功登录时重定向回来)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-20
  • 2018-05-03
  • 2021-05-02
  • 2017-03-18
  • 1970-01-01
相关资源
最近更新 更多