背景:我有一个为我的客户提供服务的网络应用程序。
动机:现在我想借助API(WCF和Web API)来公开该服务。该服务的消费者需要进行身份验证。
问题: API的大多数消费者都来自我的网络应用客户。
我不希望一个客户端有2个密码,一个用于Web App,另一个用于API。
如何与其他项目共享Web App(MVC5)数据库?,例如WCF。
我需要在我的WCF中运行两个与Web App完全相同的方法:
这个方法在我的项目中实现如下:
注册
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);
}
}
答案 0 :(得分:0)
您可以创建单独的身份数据库,并让所有用户对其进行身份验证。创建UserManager / RoleManager等时,可以将连接字符串指向标识数据库
答案 1 :(得分:0)
在阅读了几篇文章后,我了解了密码验证程序的工作原理,并且在Microsoft的开源代码的帮助下,我设法构建了一个负责密码验证的类。
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;
}
存储在DB中的字符串包含带有salt的密码的哈希值,这是一个示例: 我们在DB中有一个字符串:123456789,字符串的一部分是哈希密码,部分是盐,在我们的例子中,假设123456是哈希密码而789是盐。 (在Microsoft算法中,保存salt的字符数始终相同,但可以手动更改)