如何在IdentityServer4中与非标准用户存储进行交互?

时间:2017-02-02 19:01:03

标签: asp.net-core owin identityserver4

我想使用IdentityServer4来实现OIDC身份验证,以便将来开发。我们有一个现有的自行开发的成员资格模式,它是在Asp.Net成员资格或Aspnet Identity之前实现的。我们使用现有成员资格模式对遗留应用程序进行身份验证和授权用户。

如何为用户存储实现指向旧架构的适配器(假设带有用户名和密码列的Users表)?我见过的唯一例子是InMemory用户和AspnetIdentity。

3 个答案:

答案 0 :(得分:2)

我需要适应IdentityServer4的遗留系统需要三条信息来验证用户:公司标识符,用户名和密码。

我们的系统有一个master数据库,其中包含一个Companies表,其中包含公司特定数据库(租户)的连接字符串。每个租户数据库都有一个Users表,其中包含用户配置文件信息,包括用户名和密码。

使用Scott Brady的回答我能够实现一个适合我们情况的IResourceOwnerPasswordValidator,但不是没有挖掘IdentityModel.TokenClient源代码。我发现令牌端点(token_endpoint:" http:/// connect / token")采用URL编码形式并序列化键/值对并使其在IResourceOwnerPasswordValidator.ValidateAsync中可用Request.Raw参数的ResourceOwnerPasswordValidationContext属性。

以下是我的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 密码客户端密钥。我将动词设置为POST并指定了一个&#34; x-www-form-urlencoded&#34;身体。端点需要username,password,grant_type和scope的最小值。我添加了公司键/值参数:

Postman body configuration

答案 1 :(得分:1)

要与您自己的用户存储集成,您需要创建IProfileService的实现(如果需要使用资源所有者授权类型,还可以选择IResourceOwnerPasswordValidator),然后在{{1 }}

例如:IServiceCollection

答案 2 :(得分:0)

我有类似的实施要求,但使用acr_values标头与租户:company1作为公司标识符不会更加抱怨吗?