【问题标题】:How to interface with non-standard User store in IdentityServer4?如何与 IdentityServer4 中的非标准用户存储接口?
【发布时间】:2017-06-19 23:58:54
【问题描述】:

我想使用 IdentityServer4 来实现 OIDC 认证,以备将来开发。我们有一个在 Asp.Net 成员资格或 Aspnet Identity 之前实施的现有本土成员资格模式。我们有旧版应用程序使用现有的成员模式来对用户进行身份验证和授权。

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

【问题讨论】:

    标签: asp.net-core owin identityserver4


    【解决方案1】:

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

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

    使用 Scott Brady 的回答,我能够实现适用于我们情况的 IResourceOwnerPasswordValidator,但并非没有深入研究 IdentityModel.TokenClient 源代码。我发现令牌端点 (token_endpoint: "http:///connect/token") 采用 URL 编码形式并序列化键/值对,并通过 Request.Raw 的属性使它们在 IResourceOwnerPasswordValidator.ValidateAsync 中可用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 中,我选择了基本身份验证作为授权类型。 用户名ClientId密码Client Secret。我将动词设置为 POST 并指定了“x-www-form-urlencoded”正文。端点需要用户名、密码、授权类型和范围的最小值。我添加了一个公司键/值参数:

    【讨论】:

      【解决方案2】:

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

      例如:services.AddTransient&lt;IProfileService, GabeFcCustomProfileService&gt;();

      【讨论】:

      • 感谢您的提示,我找到了一个很好的video,它准确地描述了如何实现IResourceOwnerPasswordValidatorIProfileService,但我还有一个问题希望您能帮助我。是否可以在登录表单中添加另一个字段?我们遗留系统中的用户由公司、用户名和密码标识。我需要让公司从登录到ResourceOwnerPasswordValidator 才能唯一选择用户。
      • 没关系,我使用 Quickstart UI 作为修改 AccountController 上的登录页面和登录操作的基础,以使用公司的值以及用户名和密码。
      【解决方案3】:

      我有类似的实现要求,但是使用带有tenant:company1 作为公司标识符的acr_values 标头会不会更抱怨?

      【讨论】:

      • 我真的不太了解 CORS,但我认为访问控制标头在这种情况下不会有用。首先,我不确定这是传递附加标识参数的适当方式。我们用户的主键实际上是公司+用户名。但我也不认为可以从 ResourceOwnerPasswordValidator.ValidateAsync() 方法中访问这些标头。从我在下面发布的代码var company = context.Request.Raw.Get("company"); 来看,Raw 是一个 NameValueCollection,它是原始请求正文的内容。
      猜你喜欢
      • 2011-08-25
      • 2015-03-15
      • 2013-06-01
      • 2023-03-27
      • 1970-01-01
      • 1970-01-01
      • 2016-08-05
      • 2019-07-16
      • 1970-01-01
      相关资源
      最近更新 更多