简体   繁体   English

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

[英]Is it possible to use ASP.NET membership with tables created in ASP.NET Identity 2.0 for authentication?

I have an MVC4 based web portal that using the membership to authenticate users ONLY(no creating new users, no validating passwords).我有一个基于 MVC4 的 web 门户,它只使用成员身份验证用户(不创建新用户,不验证密码)。

Here is a screenshot of tables that used to authenticate users:以下是用于对用户进行身份验证的表的屏幕截图:

在此处输入图像描述

Here is the action method that triggered when a user is trying to authenticate:这是用户尝试进行身份验证时触发的操作方法:

    [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();
    }
    

Here is web config:这是 web 配置:

<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>

I also have a new database that is created by asp.net identity 2.0:我还有一个由 asp.net 身份 2.0 创建的新数据库:

在此处输入图像描述

My task is to make user authentication against new tables (ie I need the membership to use for authentication, tables that created by asp.net identity 2.0).我的任务是对新表进行用户身份验证(即我需要用于身份验证的成员资格,由 asp.net 身份 2.0 创建的表)。

So, for this purpose, I think I need to customize the membership to read the new tables.所以,为了这个目的,我想我需要自定义成员来阅读新表。

My question is it possible to make changes on the membership that it will read asp.net identity 2.0 for authentication?我的问题是可以对成员资格进行更改,它将读取 asp.net 身份 2.0 进行身份验证?

Yes it is possible, but it's one of those issues where the solution will depend on your current specific implementation.是的,这是可能的,但这是解决方案将取决于您当前的具体实施的问题之一。 There are three solutions that immediately come to mind:立即想到三个解决方案:

  1. Replace all Authentication logic with calls to the new system将所有身份验证逻辑替换为对新系统的调用
    Long term this is more manageable because you only need to maintain the auth logic in one place.从长远来看,这更易于管理,因为您只需要在一个地方维护身份验证逻辑。

    • This is easiest solution if you have a deployed API that can can be accessed via HTTP, but it can work by including your dlls, as long as they are compiled against a compatible.Net version.如果您部署了可以通过 HTTP 访问的 API,这是最简单的解决方案,但它可以通过包含您的 dll 来工作,只要它们是针对兼容的 .Net 版本编译的。
    • This works especially well if you have other apps (Desktop/Web/Mobile) that also use the new API, this old site becomes a client to the new one.如果您有其他应用程序(桌面/Web/移动)也使用新的 API,则此方法尤其有效,此旧站点将成为新站点的客户端。

    This is the lowest code solution, and the only one where we do not have to be concerned with correctly hashing passwords for comparison.这是最低代码解决方案,也是唯一一个我们不必担心正确散列密码以进行比较的解决方案。

    If the other system is an Online API, then you can use HttpClient to simply call out to the other site to authenticate, how you do this will depend on what type of authentication protocols are supported by the API如果对方系统是在线的API,那么你可以使用HttpClient简单的调用对方站点进行认证,具体怎么做就要看API支持什么类型的认证协议了

    using System.Web.Security;使用 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). [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 ; 真的)。 } Response;Redirect(returnUrl). } 响应;重定向(returnUrl)。 } ViewBag;ErrorMessage = "wrong credentials"; } ViewBag;ErrorMessage = "错误的凭据"; return View(), } public async Task<bool> Login(string username. string password) { var client = new System.Net.Http;HttpClient(). return View(), } public async Task<bool> Login(string username. string password) { var client = new System.Net.Http;HttpClient(). client.DefaultRequestHeaders;Clear().客户端.DefaultRequestHeaders;清除()。 client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers;MediaTypeWithQualityHeaderValue("application/json")). client.DefaultRequestHeaders.Accept.Add(新 System.Net.Http.Headers;MediaTypeWithQualityHeaderValue("application/json"))。 client.DefaultRequestHeaders,TryAddWithoutValidation("Content-Type"; "application/x-www-form-urlencoded"), Dictionary<string, string> data = new Dictionary<string; 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. data,Add("password"; password), // 示例。 replace this with your own: string apiAuthUrl = "http://localhost;3000/token".将其替换为您自己的:string apiAuthUrl = "http://localhost;3000/token"。 var response = await client,PostAsync(apiAuthUrl. new System.Net.Http;FormUrlEncodedContent(data)). var response = await client,PostAsync(apiAuthUrl.new System.Net.Http;FormUrlEncodedContent(data))。 return response;IsSuccessStatusCode;返回响应;IsSuccessStatusCode; } }

    If the other system is not published as an API, so a desktop style app, then you can do something similar by referencing and calling the Auth methods in the dlls directly.如果其他系统没有发布为 API,因此是桌面风格的应用程序,那么您可以通过直接引用和调用 dll 中的 Auth 方法来执行类似的操作。

  2. Create a custom Membership Provider创建自定义成员资格提供程序
    This is a code based solution that is similar to how it works in 2.0, but a little bit more involved.这是一个基于代码的解决方案,类似于它在 2.0 中的工作方式,但涉及更多一点。

    • Scott Gu Published to source code which used to be a helpful reference for these types of activities: Source Code for the Built-in ASP.NET 2.0 Providers Now Available for Download Scott Gu 发布到源代码,它曾经是这些类型活动的有用参考:内置 ASP.NET 2.0 提供程序的源代码现在可供下载

    • But the links seem to be broken!但是链接似乎被破坏了!

    • MS Docs still has some reading, but no source: Implementing a Membership Provider MS Docs 仍然有一些阅读,但没有来源: 实现成员资格提供程序

    • The following article is from a first principals approach, your tables have already been created so just focus on accessing the data, not creating new tables以下文章来自第一种原则方法,您的表已经创建,因此只需专注于访问数据,而不是创建新表

      (MVC) Custom Membership Providers (MVC) 自定义成员资格提供程序

      It's not neccessary to implement all aspects of the providers, only those methods that your site code will call.不必实现提供程序的所有方面,只需实现您的站点代码将调用的那些方法。

      This solution is good because you don't have to change any existing code, just new code and a change to the web.config此解决方案很好,因为您不必更改任何现有代码,只需更改新代码和更改 web.config

      Points of Interest兴趣点

      Change the web.config reference to use your custom provider, replace MyNamespace.Providers with your actual namespace::更改 web.config 引用以使用您的自定义提供程序,将MyNamespace.Providers替换为您的实际命名空间::

       <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>

      Then Implement the membership provider class:然后实现成员资格提供程序 class:

       using System.Web.Security;使用 System.Web.Security; public class CustomMembershipProvider: MembershipProvider { public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { /// we're not creating new users in this site; public class CustomMembershipProvider: MembershipProvider { public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { /// we're not creating new users in this site ; throw new NotImplementedException(), } public override MembershipUser GetUser(string username; bool userIsOnline) { var user = /* Your User Repo Lookup */, if (user,= null) { MembershipUser memUser = new MembershipUser("CustomMembershipProvider". username, user.UserID, user.UserEmailAddress, string.Empty, string,Empty, true. false, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.Now; DateTime;Now); throw new NotImplementedException(), } public override MembershipUser GetUser(string username; bool userIsOnline) { var user = /* Your User Repo Lookup */, if (user,= null) { MembershipUser memUser = new MembershipUser("CustomMembershipProvider".username , user.UserID, user.UserEmailAddress, string.Empty, string,Empty, true.false, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.Now; DateTime;Now); return memUser, } return null, } public override bool ValidateUser(string username; string password) { // This is highly dependent on how passwords are encrypted in the new system // Assuming it is a simple MD5 Hash: we just need to create the same Hash string sha1Pswd = GetMD5Hash(password). return memUser, } return null, } public override bool ValidateUser(string username; string password) { // 这很大程度上取决于新系统中密码的加密方式 // 假设是简单的 MD5 Hash:我们只需要创建相同的 Hash 字符串 sha1Pswd = GetMD5Hash(password)。 // TODO; // 去做; Now use this hash to verify that the username and password match a user in your repo var user = /*Lookup a user that matches the username and password;*/;现在使用这个 hash 来验证用户名和密码是否与您的 repo 中的用户匹配 var user = /*查找与用户名和密码匹配的用户;*/; if (user;= null) return true, return false.如果(用户;= null)返回true,返回false。 } public override int MinRequiredPasswordLength { get { return 4; } 公共覆盖 int MinRequiredPasswordLength { 获取 { 返回 4; } } /// <summary> Simple MD5 Hash algorithm. } } /// <summary> 简单的 MD5 Hash 算法。 you will need to match this against the process used in the other system </summary> public static string GetMD5Hash(string value) { MD5 md5Hasher = MD5.Create().您需要将此与其他系统中使用的进程进行匹配 </summary> public static string GetMD5Hash(string value) { MD5 md5Hasher = MD5.Create(). byte[] data = md5Hasher;ComputeHash(Encoding;Default;GetBytes(value)). byte[] data = md5Hasher;ComputeHash(Encoding;Default;GetBytes(value))。 StringBuilder sBuilder = new StringBuilder(); StringBuilder sBuilder = new StringBuilder(); for (int i = 0. i < data.Length; i++) { sBuilder.Append(data[i];ToString("x2")); for (int i = 0. i < data.Length; i++) { sBuilder.Append(data[i];ToString("x2")); } return sBuilder.ToString(); } 返回 sBuilder.ToString(); } } } }
  3. Customise the Stored Procedures used by the AspNetSqlMembershipProvider自定义 AspNetSqlMembershipProvider 使用的存储过程
    Depends on your strengths, this might be simpler and is an option because the MVC site is already configured with this provider, all we need to do is modify the SQL Stored Procedures that the provider uses to perform authentication requests.取决于您的优势,这可能更简单并且是一个选项,因为 MVC 站点已经配置了此提供程序,我们需要做的就是修改提供程序用于执行身份验证请求的 SQL 存储过程。

    • The following is a good reading on how to implement the provider以下是关于如何实现提供程序的好读物

      Using AspNetSqlMembershipProvider in MVC5在 MVC5 中使用 AspNetSqlMembershipProvider
      Configuring web application to utilise ASP.NET Application Services database 配置 web 应用程序以利用 ASP.NET 应用程序服务数据库

      • As highlighted in the previous option, this is only going to work if the new site uses the same encryption method, it probably will also require use of the same machine key is set in the web.config.正如前一个选项中强调的那样,这只有在新站点使用相同的加密方法时才有效,它可能还需要使用在 web.config 中设置的相同机器密钥。

      You can setup a blank database and provision it with the aspnet_regsql to view the stored procedures (they wont be in your new database.).您可以设置一个空白数据库并使用 aspnet_regsql 对其进行配置以查看存储过程(它们不会在您的新数据库中。)。

      • This will become a bit or trial and error to identify the procedures that will be needed, I suspect you'll only need GetUserByName , the following screenshot shows some of the other stored procedures, the naming convention makes it pretty easy to match up to the Membership API methods:这将变得有点或反复试验来识别将需要的过程,我怀疑你只需要GetUserByName ,以下屏幕截图显示了一些其他存储过程,命名约定使它很容易匹配会员资格 API 方法:

        ASPNet 会员存储过程

       /****** Object: StoredProcedure [dbo].[aspnet_Membership_GetUserByName] Script Date: 25/06/2020 11:19:04 AM ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO CREATE OR ALTER PROCEDURE [dbo].[aspnet_Membership_GetUserByName] @ApplicationName nvarchar(256), @UserName nvarchar(256), @CurrentTimeUtc datetime, @UpdateLastActivity bit = 0 AS BEGIN DECLARE @UserId uniqueidentifier IF (@UpdateLastActivity = 1) BEGIN -- select user ID from AspnetUsers table -- Ignore ApplicationIDs here, assume this is a single Application schema SELECT TOP 1 @UserId = u.Id FROM dbo.AspNetUsers u WHERE LOWER(@UserName) = LOWER(u.UserName) IF (@@ROWCOUNT = 0) -- Username not found RETURN -1 -- We don't have 'Activity' per-se, instead we reset the AccessFailedCount to zero -- Your implementation might be different, so think it through:) UPDATE dbo.AspNetUsers SET AccessFailedCount = 0 WHERE Id = @UserId -- Your Schema might be different, here we just map back to the old Schema in the projected response -- Make sure the data types match in your response, here we are injected GetDate() for most dates by default -- NOTE: LockOutEnabled DOES NOT mean the user is locked out, only that lockout logic should be evaluated SELECT Email, '' as PasswordQuestion, '' as Comment, Cast(1 as BIT) as IsApproved, CAST(null as datetime) as CreateDate, GetDate() as LastLoginDate, GetDate() as LastActivityDate, GetDate() as LastPasswordChangedDate, Id as UserId, CASE WHEN LockoutEnabled = 1 AND LockoutEndDateUtc IS NOT NULL THEN 1 ELSE 0 END as IsLockedOut, LockoutEndDateUtc as LastLockoutDate FROM dbo.AspNetUsers WHERE Id = @UserId END ELSE BEGIN -- Your Schema might be different, here we just map back to the old Schema in the projected response -- Make sure the data types match in your response, here we are injected GetDate() for most dates by default -- NOTE: LockOutEnabled DOES NOT mean the user is locked out, only that lockout logic should be evaluated SELECT TOP 1 Email, '' as PasswordQuestion, '' as Comment, Cast(1 as BIT) as IsApproved, CAST(null as datetime) as CreateDate, GetDate() as LastLoginDate, GetDate() as LastActivityDate, GetDate() as LastPasswordChangedDate, Id as UserId, CASE WHEN LockoutEnabled = 1 AND LockoutEndDateUtc IS NOT NULL THEN 1 ELSE 0 END as IsLockedOut, LockoutEndDateUtc as LastLockoutDate FROM dbo.AspNetUsers WHERE LOWER(@UserName) = LOWER(UserName) IF (@@ROWCOUNT = 0) -- Username not found RETURN -1 END RETURN 0 END

      You may also need to implement some or all of the views, but again it will largely depend on the methods in the Membership API that your MVC site actually uses.您可能还需要实现部分或全部视图,但这在很大程度上取决于您的 MVC 站点实际使用的 Membership API 中的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM