从外部项目连接到现有 MVC 5 数据库(身份模型)

Connect to an Existing MVC 5 DB (Identity model) From External Project

背景:我有一个 Web 应用程序可以为我的客户提供服务。

动机: 现在我想借助 API(WCF 和 Web API)公开该服务。服务的消费者将需要进行身份验证。

问题: API 的大多数消费者将来自我的 Web App 客户。

我不希望一个客户端有 2 个密码,一个用于 Web 应用程序,一个用于 API。

如何与其他项目共享 Web 应用程序 (MVC5) 数据库? 例如 WCF。

我的 WCF 中需要两个 运行 与 Web 应用程序完全一样的方法:

这些方法在我的项目中实现如下:

报名:

 public async Task<ActionResult> Register(RegisterViewModel model)
    {
        if (ModelState.IsValid)
        {
            var user = new ApplicationUser { UserName = model.UserName, Email = model.Email, OrganizationID = "10", DateJoin = DateTime.Now, LockoutEndDateUtc=DateTime.UtcNow.AddYears(5),LockoutEnabled=false};
            var result = await UserManager.CreateAsync(user, model.Password);
            if (result.Succeeded)
            {

                IdentityResult resultClaim = await UserManager.AddClaimAsync(user.Id, new Claim("OrgID", "10"));

                if(resultClaim.Succeeded)
                {
                    UserManager.AddToRole(user.Id, "guest");
                    await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
                    return RedirectToAction("Index", "Home");
                }


            }
            AddErrors(result);
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

登录:

  public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {

        if (!ModelState.IsValid || User.Identity.IsAuthenticated)
        {
            return View(model);
        }

        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, change to shouldLockout: true
        var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
        switch (result)
        {
           case SignInStatus.Success:
                Session["Timezone"] = model.offSet;
                    return RedirectToLocal(returnUrl);

            case SignInStatus.LockedOut:
                return View("Lockout");

            case SignInStatus.RequiresVerification:
                return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });

            case SignInStatus.Failure:
            default:
                ModelState.AddModelError("", "Invalid login attempt.");
                return View(model);
        }
    }

您可以创建一个单独的身份数据库,并让您的所有用户对其进行身份验证。当您创建 UserManager/RoleManager 等时,您可以将连接字符串指向身份数据库

阅读了几篇文章后,我了解了密码验证器的工作原理,并且在 Microsoft 开源代码的帮助下,我设法构建了一个 class 来进行密码验证。

用户验证class:

 public class UserValidation 
{
    public override void Validate(string userName, string password)
    {
        if (null == userName || null == password)
        {
            throw new ArgumentNullException();
        }

        string hashPassword = DataQueries.GetHashPassword(userName);

        if (!VerifyHashedPassword(hashPassword, password))
            throw new FaultException("Unknown Username or incorrect Password");

    }

    private static string HashPassword(string password)
    {
        byte[] salt;
        byte[] buffer2;
        if (password == null)
        {
            throw new ArgumentNullException("password");
        }
        using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, 0x10, 0x3e8))
        {
            salt = bytes.Salt;
            buffer2 = bytes.GetBytes(0x20);
        }
        byte[] dst = new byte[0x31];
        Buffer.BlockCopy(salt, 0, dst, 1, 0x10);
        Buffer.BlockCopy(buffer2, 0, dst, 0x11, 0x20);
        return Convert.ToBase64String(dst);
    }

    private static bool VerifyHashedPassword(string hashedPassword, string password)
    {
        byte[] buffer4;
        if (hashedPassword == null)
        {
            return false;
        }
        if (password == null)
        {
            throw new ArgumentNullException("password");
        }
        byte[] src = Convert.FromBase64String(hashedPassword);
        if ((src.Length != 0x31) || (src[0] != 0))
        {
            return false;
        }
        byte[] dst = new byte[0x10];
        Buffer.BlockCopy(src, 1, dst, 0, 0x10);
        byte[] buffer3 = new byte[0x20];
        Buffer.BlockCopy(src, 0x11, buffer3, 0, 0x20);
        using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, dst, 0x3e8))
        {
            buffer4 = bytes.GetBytes(0x20);
        }
        return ByteArraysEqual(buffer3, buffer4);
    }

    private static bool ByteArraysEqual(byte[] b1, byte[] b2)
    {
        if (b1 == b2) return true;
        if (b1 == null || b2 == null) return false;
        if (b1.Length != b2.Length) return false;
        for (int i = 0; i < b1.Length; i++)
        {
            if (b1[i] != b2[i]) return false;
        }
        return true;
    }

Microsoft 密码验证算法背后的思想是:

存储在数据库中的字符串包含加盐的密码哈希值,这里是一个例子: 我们在数据库中有一个字符串:123456789,字符串的一部分是哈希密码,一部分是盐,在我们的示例中,假设 123456 是哈希密码,789 是盐。 (持有盐的字符数在 Microsoft 算法中始终相同,但可以手动更改)