问题描述
我正在创建一个 SSO 服务器,将所有用户集中在 ActiveDirectory(AD) 中并在那里管理他们,而不是每个特定应用程序的数据库.
I'm creating an SSO server, to centralize all users in ActiveDirectory(AD) and manage them there instead of the database of each specific application.
为了制作这个服务器,我使用了 IdentityServer4(Idsr4) 和 Ldap/AD 扩展
To made this server I used IdentityServer4(Idsr4) with Ldap/AD Extension
我已将 Idsr4 设置为使用基于 AD 的身份(这是中心化身份"),用户现在可以使用自己的 AD 登录名/密码登录 Idsr4
I've setted the Idsr4 to use identity based on AD (this is "centralized identity"), and users now can login on Idsr4 with own AD login/ password
现在的问题是如何将集中身份映射到应用程序.我想在多个应用程序中使用相同的身份用户.
The question now is how to map the centralized identity to applications. I want to use same identity user in several applications.
我通读了 IdentityServer4 的文档,但找不到与提议结构相关的任何内容.
I read through the documentation of IdentityServer4 but could not find anything related to a proposed structure.
有没有人有一个清晰的结构设置可以用来理解整个设置?(分离如 Asp.Net MVC Boilerplate、IdentityServer4、Protected Api.)
Does anybody have a clear structure setup which could be used to understand the whole setup? (Separation like Asp.Net MVC Boilerplate, IdentityServer4, Protected Api.)
IdentityServer4 配置:
IdentityServer4 Config:
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:
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 userinfo 端点中接受用户的声明,如下所示:
UPDATE
I was reading the documentation on OpenID Connect and saw that it is possible to create notifications for httpContext to take the user's claims in the Idsrv4 userinfo endpoint like this:
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,
UPDATE 2
Thank you @Khanh TO,
我完全按照你的建议做了,我保留了每个应用程序的数据库
I did exactly what you recommended, I kept the database of each application
但是为了不再通过应用程序数据库管理用户,我硬编码了一个从 idsr4 userinfo 端点获取的方法
However to no longer manage the users by the application database, I hardcode a method that takes from the idsr4 userinfo endpoint
在 abpUsers 表中创建或更新用户所需的信息,然后应用程序解释数据并执行必要的操作
The information needed to create or update a user in the abpUsers table, then the application interprets the data and does the necessary actions
更具体地说:在我发送到客户端的 AccountController
的 redirect_uri
中,我有一个 ActionResult
来完成所有这些工作,调用必要的方法来创建/更新客户端用户表上的用户
More specifically:In the redirect_uri
I send to the AccountController
of my client, there I have an ActionResult
that does all this work calling the necessary methods to create/update an user on client userstable
推荐答案
我认为GrantType.ResourceOwnerPassword
流程不支持AD
登录并且不支持通过 UseOpenIdConnectAuthentication
和 ,您可以使用 Implicit
或 Hybrid
流.
对客户端 mvc 应用程序进行身份验证后,您可以查看 HttpContext.User
中的任何声明,并找到作为用户身份的正确声明值(它们只是声明,无需创建本地帐户)
I think the GrantType.ResourceOwnerPassword
flow doens't support AD
login and not support by the UseOpenIdConnectAuthentication
neither , you can use Implicit
or Hybrid
flow.
Once you authenticate the to your client mvc app, you can view any claims in HttpContext.User
and find the correct claim value as user's identity (they are just claims , and no need to create a local account)
这篇关于如何将 IdentityServer4 身份映射到任何 WebApp(.Net MVC Boilerplate、.Net Core Boilerplate)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!