是否可以将 ASP.NET 成员身份与 ASP.NET Identity 2.0 中创建的表一起使用进行身份验证?

Mic*_*ael 5 .net c# asp.net asp.net-mvc asp.net-identity

我有一个基于 MVC4 的 Web 门户,仅使用成员资格来验证用户身份(不创建新用户,不验证密码)。

以下是用于验证用户身份的表的屏幕截图:

在此输入图像描述

以下是用户尝试进行身份验证时触发的操作方法:

    [HttpPost]
    public ActionResult Login(string username, string password, string[] rememberMe, string returnUrl)
    {
        if(Membership.ValidateUser(username, password))
        {
            if (rememberMe != null)
            {
                FormsAuthentication.SetAuthCookie(username, true);
            }
            Response.Redirect(returnUrl);
        }
        ViewBag.ErrorMessage = "wrong credentials";
        return View();
    }
    
Run Code Online (Sandbox Code Playgroud)

这是网络配置:

<membership defaultProvider="AspNetSqlMembershipProvider" userIsOnlineTimeWindow="15">
  <providers>
    <clear />
    <add name="AspNetSqlMembershipProvider" 
         connectionStringName="SiteSqlServer" 
         enablePasswordRetrieval="false" 
         enablePasswordReset="true" 
         requiresQuestionAndAnswer="false"
         applicationName="/" 
         requiresUniqueEmail="true" 
         passwordFormat="Hashed" 
         maxInvalidPasswordAttempts="8" 
         minRequiredPasswordLength="4" 
         minRequiredNonalphanumericCharacters="0" 
         passwordAttemptWindow="10" 
         passwordStrengthRegularExpression="" 
         type="System.Web.Security.SqlMembershipProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d43e4e" />
  </providers>
</membership>
Run Code Online (Sandbox Code Playgroud)

我还有一个由 ASP.NET Identity 2.0 创建的新数据库:

在此输入图像描述

我的任务是针对新表进行用户身份验证(即我需要用于身份验证的成员资格,由 asp.net Identity 2.0 创建的表)。

因此,为此目的,我认为我需要自定义成员资格以读取新表。

我的问题是否可以对成员身份进行更改,使其读取 asp.net Identity 2.0 进行身份验证?

Chr*_*ler 4

是的,这是可能的,但这是解决方案取决于您当前的具体实现的问题之一。我立即想到了三种解决方案:

  1. 将所有身份验证逻辑替换为对新系统的调用
    从长远来看,这更易于管理,因为您只需在一处维护身份验证逻辑。

    • 如果您有一个可以通过 HTTP 访问的已部署 API,那么这是最简单的解决方案,但它可以通过包含您的 dll 来工作,只要它们是针对兼容的 .Net 版本进行编译的。
    • 如果您有其他也使用新 API 的应用程序(桌面/网络/移动),则此功能尤其有效,该旧站点将成为新站点的客户端。

    这是最低代码的解决方案,也是唯一一个我们不必关心正确散列密码以进行比较的解决方案。

    如果对方系统是在线 API,那么您可以使用 HttpClient 简单地调用其他站点进行身份验证,具体操作方式取决于 API 支持什么类型的身份验证协议

    使用 System.Web.Security;
    
    [http邮报]
    公共异步任务 <ActionResult> 登录(字符串用户名,字符串密码,字符串[] RememberMe,字符串returnUrl)
    {
        if(等待ValidateUser(用户名,密码))
        {
            如果(记住我!= null)
            {
                FormsAuthentication.SetAuthCookie(用户名, true);
            }
            Response.Redirect(returnUrl);
        }
        ViewBag.ErrorMessage = "凭据错误";
        返回视图();
    }
    
    公共异步任务<bool>登录(字符串用户名,字符串密码)
    {
        var client = new System.Net.Http.HttpClient();
        client.DefaultRequestHeaders.Clear();
        client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
        client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded");
        Dictionary<字符串, 字符串> data = new Dictionary<字符串, 字符串>();
        data.Add("grant_type", "密码");
        data.Add("用户名", 用户名);
        数据.Add("密码", 密码);
    
        // 例如,将其替换为您自己的。
        字符串 apiAuthUrl = "http://localhost:3000/token";
    
        var response = wait client.PostAsync(apiAuthUrl, new System.Net.Http.FormUrlEncodedContent(data));
        返回响应.IsSuccessStatusCode;
    }
    

    如果其他系统没有作为 API 发布,即桌面样式应用程序,那么您可以通过直接引用和调用 dll 中的 Auth 方法来执行类似的操作。

  2. 创建自定义成员资格提供程序
    这是一个基于代码的解决方案,与 2.0 中的工作方式类似,但涉及的内容更多一些。

    • Scott Gu 发布的源代码曾经是此类活动的有用参考:内置 ASP.NET 2.0 提供程序的源代码现已可供下载

    • 但链接似乎已损坏!

    • MS Docs 仍然有一些阅读内容,但没有来源:Implementing a Membership Provider

    • 以下文章来自第一个原理方法,您的表已经创建,因此只需关注访问数据,而不是创建新表

      (MVC) 自定义会员提供商

      不必实现提供程序的所有方面,只需实现站点代码将调用的那些方法即可。

      这个解决方案很好,因为您不必更改任何现有代码,只需添加新代码并更改 web.config

      兴趣点

      更改 web.config 引用以使用您的自定义提供程序,替换MyNamespace.Providers为您的实际命名空间!:

      using System.Web.Security;
      
      [HttpPost]
      public async Task<ActionResult> Login(string username, string password, string[] rememberMe, string returnUrl)
      {
          if(await ValidateUser(username, password))
          {
              if (rememberMe != null)
              {
                  FormsAuthentication.SetAuthCookie(username, true);
              }
              Response.Redirect(returnUrl);
          }
          ViewBag.ErrorMessage = "wrong credentials";
          return View();
      }
      
      public async Task<bool> Login(string username, string password)
      {
          var client = new System.Net.Http.HttpClient();
          client.DefaultRequestHeaders.Clear();
          client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
          client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded");
          Dictionary<string, string> data = new Dictionary<string, string>();
          data.Add("grant_type", "password");
          data.Add("username", username);
          data.Add("password", password);
      
          // Example, replace this with your own.
          string apiAuthUrl = "http://localhost:3000/token";
      
          var response = await client.PostAsync(apiAuthUrl, new System.Net.Http.FormUrlEncodedContent(data));
          return response.IsSuccessStatusCode;
      }
      

      然后实现会员提供者类:

      使用 System.Web.Security;
      
      公共类 CustomMembershipProvider :MembershipProvider
      {
          公共覆盖MembershipUser CreateUser(字符串用户名,字符串密码,
                 字符串电子邮件、字符串密码问题、字符串密码答案、
                 bool isApproved,对象providerUserKey,out MembershipCreateStatus状态)
          {
              /// 我们不会在此站点中创建新用户!
              抛出新的NotImplementedException();             
          }
      
          公共覆盖 MembershipUser GetUser(字符串用户名, bool userIsOnline)
          {
              var user = /* 您的用户存储库查找 */;
      
              if (用户!= null)
              {
                  MembershipUser memUser = new MembershipUser("CustomMembershipProvider",
                                                 用户名、用户.用户ID、用户.用户电子邮件地址、
                                                 字符串.空,字符串.空,
                                                 true、false、DateTime.MinValue、
                                                 日期时间.MinValue,
                                                 日期时间.MinValue,
                                                 日期时间.Now, 日期时间.Now);
                  返回内存用户;
              }
              返回空值;
          }
      
          公共覆盖 bool ValidateUser(字符串用户名,字符串密码)
          {
              // 这很大程度上取决于新系统中密码的加密方式
              // 假设是一个简单的MD5 Hash,我们只需要创建相同的Hash              
              字符串 sha1Pswd = GetMD5Hash(密码);
      
              // TODO:现在使用此哈希来验证用户名和密码是否与存储库中的用户匹配
              var user = /*查找与用户名和密码匹配的用户*/;
              if (用户!= null)
                  返回真;
              返回假;
          }
      
          公共覆盖 int MinRequiredPasswordLength
          {
              得到{返回4; }
          }
      
          /// <summary> 简单的 MD5 哈希算法,您需要将其与其他系统中使用的过程进行匹配 </summary>
          公共静态字符串GetMD5Hash(字符串值)
          {
              MD5 md5Hasher = MD5.Create();
              byte[] 数据 = md5Hasher.ComputeHash(Encoding.Default.GetBytes(value));
              StringBuilder sBuilder = new StringBuilder();
              for (int i = 0; i < data.Length; i++)
              {
                  sBuilder.Append(data[i].ToString("x2"));
              }
      
              返回 sBuilder.ToString();
          }
      }
      
      
      
  3. 自定义 AspNetSqlMembershipProvider 使用的存储过程
    取决于您的优势,这可能更简单,并且是一个选项,因为 MVC 站点已经配置了此提供程序,我们需要做的就是修改提供程序用于执行身份验证的 SQL 存储过程要求。

    • 以下是有关如何实现提供程序的好读物

      在 MVC5 中使用 AspNetSqlMembershipProvider
      配置 Web 应用程序以利用 ASP.NET 应用程序服务数据库

      • 正如上一个选项中强调的,只有当新站点使用相同的加密方法时,这才有效,它可能还需要使用 web.config 中设置的相同的计算机密钥。

      您可以设置一个空白数据库并使用 aspnet_regsql 配置它来查看存储过程(它们不会位于您的新数据库中!)。

      • 这将变得有点或反复试验来识别所需的过程,我怀疑您只需要GetUserByName,下面的屏幕截图显示了一些其他存储过程,命名约定使其很容易匹配会员API方法:

        ASPNet 会员存储过程

      <membership defaultProvider="CustomMembershipProvider" userIsOnlineTimeWindow="15">
        <providers>
          <clear/>
          <add name="CustomMembershipProvider" 
              type="MyNamespace.Providers.CustomMembershipProvider"
              connectionStringName="SiteSqlServer"
              enablePasswordRetrieval="false"
              enablePasswordReset="true"
              requiresQuestionAndAnswer="false"
              requiresUniqueEmail="true"
              maxInvalidPasswordAttempts="8"
              minRequiredPasswordLength="4"
              minRequiredNonalphanumericCharacters="0"
              passwordAttemptWindow="10"
              applicationName="DotNetNuke" />
        </providers>
      </membership>
      
      Run Code Online (Sandbox Code Playgroud)

      您可能还需要实现部分或全部视图,但这在很大程度上取决于您的 MVC 站点实际使用的 Membership API 中的方法。