如何与 IdentityServer4 中的非标准用户存储接口?
How to interface with non-standard User store in IdentityServer4?
想用IdentityServer4实现OIDC认证,方便以后开发。我们有一个在 Asp.Net 成员资格或 Aspnet Identity 之前实施的现有本土成员资格模式。我们的遗留应用程序使用现有的成员资格架构来验证和授权用户。
如何为用户存储实现指向旧架构的适配器(假设用户 table 具有用户名和密码列)?我见过的唯一示例是 InMemory 用户和 AspnetIdentity。
要与您自己的用户存储集成,您需要创建 IProfileService
的实现(如果您需要使用资源所有者授权类型,还可以选择 IResourceOwnerPasswordValidator
),然后在 IServiceCollection
.
例如:services.AddTransient<IProfileService, GabeFcCustomProfileService>();
我需要适应 IdentityServer4 的遗留系统需要三个信息来验证用户:公司标识符、用户名和密码。
我们的系统有一个带有公司 table 的主数据库,其中包含 company-specific 数据库(租户)的连接字符串。每个租户数据库都有一个用户 table,其中包含用户配置文件信息,包括用户名和密码。
使用 Scott Brady 的回答,我能够实现适合我们情况的 IResourceOwnerPasswordValidator
,但并非没有深入研究 IdentityModel.TokenClient 源代码。我发现令牌端点 (token_endpoint: "http:///connect/token") 采用 URL 编码形式并序列化 key/value 对并使它们在 IResourceOwnerPasswordValidator.ValidateAsync
中可用ResourceOwnerPasswordValidationContext
参数的 Request.Raw
属性。
这是我的 ResourceOwnerPasswordValidator.ValidateAsync()
实施示例,显示了我如何使用用户名和密码以外的其他参数进行身份验证:
public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
_logger.LogInformation("Begin resource owner password validation.");
var username = context.UserName;
var password = context.Password;
var company = context.Request.Raw.Get("company");
if (string.IsNullOrWhiteSpace(company))
{
_logger.LogError("'company' doesn't exist in ResourceOwnerPasswordValidationContext.Request.Raw collection.");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, "Company is required.");
return Task.FromResult(0);
}
if (string.IsNullOrWhiteSpace(username))
{
_logger.LogError("'username' is null or whitespace.");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, "Username is required.");
return Task.FromResult(0);
}
var userRepo = _userRepositoryFactory.GetUserRepositoryForCompany(company);
var user = userRepo.GetUserByUserId(username);
if (user == null)
{
_logger.LogError($"No user found for company {company} and username {username}.");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, $"No user {username} found for for {company}.");
return Task.FromResult(0);
}
_logger.LogInformation("Resource owner password validation succeeded.");
context.Result = user.Password == password ? new GrantValidationResult(context.UserName, GrantType.ResourceOwnerPassword, new [] { new Claim("company", company) }) : new GrantValidationResult(TokenRequestErrors.InvalidRequest, "Invalid username or password.");
return Task.FromResult(0);
}
我使用 Postman 通过点击令牌端点来验证我的结果。首先,我在我的 IdentityServer4 实现中配置了一个客户端:
new Client {
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email
},
ClientId = "test.client",
ClientName = "Test Client",
ClientSecrets = new List<Secret>
{
new Secret("secret".Sha256())
}
}
在 Postman 中,我选择了 Basic Auth 作为授权类型。 用户名 是 ClientId,密码 是 Client Secret.我将动词设置为 POST 并指定了一个 "x-www-form-urlencoded" 正文。端点需要用户名、密码、grant_type 和范围的最小值。我添加了公司 key/value 参数:
我有类似的实施要求,但使用 acr_values header 和 tenant:company1 作为公司标识符会不会更令人不满?
想用IdentityServer4实现OIDC认证,方便以后开发。我们有一个在 Asp.Net 成员资格或 Aspnet Identity 之前实施的现有本土成员资格模式。我们的遗留应用程序使用现有的成员资格架构来验证和授权用户。
如何为用户存储实现指向旧架构的适配器(假设用户 table 具有用户名和密码列)?我见过的唯一示例是 InMemory 用户和 AspnetIdentity。
要与您自己的用户存储集成,您需要创建 IProfileService
的实现(如果您需要使用资源所有者授权类型,还可以选择 IResourceOwnerPasswordValidator
),然后在 IServiceCollection
.
例如:services.AddTransient<IProfileService, GabeFcCustomProfileService>();
我需要适应 IdentityServer4 的遗留系统需要三个信息来验证用户:公司标识符、用户名和密码。
我们的系统有一个带有公司 table 的主数据库,其中包含 company-specific 数据库(租户)的连接字符串。每个租户数据库都有一个用户 table,其中包含用户配置文件信息,包括用户名和密码。
使用 Scott Brady 的回答,我能够实现适合我们情况的 IResourceOwnerPasswordValidator
,但并非没有深入研究 IdentityModel.TokenClient 源代码。我发现令牌端点 (token_endpoint: "http:///connect/token") 采用 URL 编码形式并序列化 key/value 对并使它们在 IResourceOwnerPasswordValidator.ValidateAsync
中可用ResourceOwnerPasswordValidationContext
参数的 Request.Raw
属性。
这是我的 ResourceOwnerPasswordValidator.ValidateAsync()
实施示例,显示了我如何使用用户名和密码以外的其他参数进行身份验证:
public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
_logger.LogInformation("Begin resource owner password validation.");
var username = context.UserName;
var password = context.Password;
var company = context.Request.Raw.Get("company");
if (string.IsNullOrWhiteSpace(company))
{
_logger.LogError("'company' doesn't exist in ResourceOwnerPasswordValidationContext.Request.Raw collection.");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, "Company is required.");
return Task.FromResult(0);
}
if (string.IsNullOrWhiteSpace(username))
{
_logger.LogError("'username' is null or whitespace.");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, "Username is required.");
return Task.FromResult(0);
}
var userRepo = _userRepositoryFactory.GetUserRepositoryForCompany(company);
var user = userRepo.GetUserByUserId(username);
if (user == null)
{
_logger.LogError($"No user found for company {company} and username {username}.");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, $"No user {username} found for for {company}.");
return Task.FromResult(0);
}
_logger.LogInformation("Resource owner password validation succeeded.");
context.Result = user.Password == password ? new GrantValidationResult(context.UserName, GrantType.ResourceOwnerPassword, new [] { new Claim("company", company) }) : new GrantValidationResult(TokenRequestErrors.InvalidRequest, "Invalid username or password.");
return Task.FromResult(0);
}
我使用 Postman 通过点击令牌端点来验证我的结果。首先,我在我的 IdentityServer4 实现中配置了一个客户端:
new Client {
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email
},
ClientId = "test.client",
ClientName = "Test Client",
ClientSecrets = new List<Secret>
{
new Secret("secret".Sha256())
}
}
在 Postman 中,我选择了 Basic Auth 作为授权类型。 用户名 是 ClientId,密码 是 Client Secret.我将动词设置为 POST 并指定了一个 "x-www-form-urlencoded" 正文。端点需要用户名、密码、grant_type 和范围的最小值。我添加了公司 key/value 参数:
我有类似的实施要求,但使用 acr_values header 和 tenant:company1 作为公司标识符会不会更令人不满?