我刚刚按照this article中的说明将Google添加为我的MVC 5应用的登录提供商。一切似乎都运行正常,但当我通过谷歌登录时,它希望我在我的应用程序中注册Google提供的电子邮件/用户名作为新帐户。如果我按原样保留电子邮件并单击“注册”按钮,它会告诉我该地址已被占用,因为我之前已在我的应用程序自己的登录提供商处注册。
如何调整MVC项目模板生成的默认代码,以便将Google登录信息与现有本地帐户相关联?
P.S。我和Facebook有完全相同的问题。
答案 0 :(得分:11)
我完全赞同@Chris Pratt提出的观点 但是,我不确定使用的代码是否足以完成OP所要求的内容。
在ExternalLoginCallback内的默认块中添加此代码应该执行作业
ApplicationUser user = await UserManager.FindByNameAsync(loginInfo.Email);
if (user != null)
{
var addLoginResult = await UserManager.AddLoginAsync(user.Id, loginInfo.Login);
if (addLoginResult.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
return RedirectToLocal(returnUrl);
}
}
答案 1 :(得分:7)
我认为Identity会按照其故意的方式处理它,因为没有真正的方法可以通过来自第三方的电子邮件来验证用户的身份。虽然风险可能相对较低,但有人可能会使用不属于他们的电子邮件地址与Facebook等第三方创建帐户,然后使用该第三方登录模拟其他网站上的帐户附在同一封电子邮件中。
因此,Identity仅允许您在登录时使用外部登录创建新帐户,而不是附加到现有帐户。但是,一旦用户通过其他方式进行身份验证,就会提供方法来关联其他登录信息。
如果您不担心相对温和的安全风险,只是假设如果电子邮件与同一个人匹配,那么您只需修改ExternalLoginCallback
中的AccountController.cs
以尝试查找用户通过电子邮件:
var user = await UserManager.FindByEmailAsync(loginInfo.Email);
然后签名:
await SignInManager.SignInAsync(user);
答案 2 :(得分:2)
以下是我能够解决您遇到的这个问题的方法。此解决方案允许您使用您的电子邮件在网站上注册,如果您尝试使用Google使用相同的电子邮件地址登录,则不会要求您注册,也不会产生任何错误;如果您在本地登录的电子邮件地址与您的Google帐户电子邮件相同,则您将被允许登录。我编辑了VS2015使用if / else语句生成的Default ExternalLoginCallBack代码,该代码检查与登录电子邮件匹配的现有电子邮件。我希望这可以帮助你解决问题,因为我有同样的问题,无法在任何地方找到答案。我的多个帖子请求被忽略了,谢天谢地,我从这篇帖子中读到了一个答案,这个答案让我得到了我自己在VS2015 Core上为我工作的解决方案。
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
if (remoteError != null)
{
ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}");
return View(nameof(Login));
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction(nameof(Login));
}
// Sign in the user with this external login provider if the user already has a login.
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
var user = await _userManager.FindByNameAsync(email);
if (user != null)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
else
{
// If user does not already exists, invite User to register.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
if (result.Succeeded)
{
_logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
email = info.Principal.FindFirstValue(ClaimTypes.Email);
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });
}
}
}
答案 3 :(得分:1)
这是MVC模板中的正常行为,因为外部登录尝试创建用户,如果用户的电子邮件(或外部标识)已经存在,请尝试登录。此外,外部登录提供程序尝试在您的应用程序(本地)中为您的外部标识分配可选且唯一的标识符。 但是,正如你所说的那样,以下是奇怪的:
如果我按原样保留电子邮件并单击“注册”按钮,它会告诉我 我已经采取了这个地址,正如我之前在我的网站上注册的那样 应用程序自己的登录提供程序。
哪个应该有效,因为每个用户的外部ID在其他站点中应该是唯一的(我相信),除非您已经注册了多个具有相同身份的外部帐户,因为表结构如下所示:
列UserId
将与下表UserId
列匹配:
当用户尝试分配重复的Username
时,将很少显示该消息,该重复Username
将使用您的电子邮件或身份名称枚举在其他网站中(例如somename@gmail.com将 somename as @Override
public View getView(int position, View view, ViewGroup parent)
{
...
...
}
@Override
public boolean isEnabled(int position ) {
if ( Item should be unclickable)
{
return = false;
}
else
{
return = true;
}
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
)
答案 4 :(得分:0)
TLDR 您需要在ExternalLoginConfirmation
函数中手动完成所有方案,并拥有一个数据库表,以便能够将成员资格用户ID与OAuth用户ID匹配。这样,您就可以将多个OAuth帐户与单个本地帐户“关联”。
以下是我们其中一个项目的代码片段 - 希望它足够清晰
public ActionResult ExternalLoginCallback()
{
var returnUrl = HttpContext.Request.QueryString["returnUrl"];
var result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
if (result.IsSuccessful == false)
{
return this.View("ExternalLoginFailure", result);
}
// Login user if provider represents a valid already registered user
if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false))
{
return this.RedirectToLocal(returnUrl);
}
// If the current user is logged in already - add new account
if (User.Identity.IsAuthenticated)
{
OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
return this.RedirectToLocal(returnUrl);
}
var membershipUser = Membership.GetUser(result.UserName);
// so user is new - then create new membership account
if (membershipUser == null)
{
MembershipCreateStatus createStatus;
membershipUser = Membership.CreateUser(username: result.UserName, password: this.GetTempPassword(), email: result.UserName, status: out createStatus);
if (createStatus == MembershipCreateStatus.Success)
{
this.emailService.SendWelcome(this, (Guid)membershipUser.ProviderUserKey);
// Associate social network account with created membership account
OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, result.UserName);
OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false);
return this.RedirectToLocal(returnUrl);
}
// The problem occured while creating membership account
this.ViewBag.Error = MembershipErrorNameProvider.FromErrorCode(createStatus);
return this.View("CreateMembershipAccountFailure");
}
// If membership account already exists -> Associate Social network account with exists membership account
OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, result.UserName);
OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false);
return this.RedirectToLocal(returnUrl);
}
和OAuthWebSecurity
是一个帮助程序类,它处理您支持的所有提供程序:
public static class OAuthWebSecurity
{
....
public static bool Login(string providerName, string providerUserId, bool createPersistentCookie)
{
var context = new HttpContextWrapper(HttpContext.Current);
var provider = GetOAuthClient(providerName);
var securityManager = new OpenAuthSecurityManager(context, provider, OAuthDataProvider);
return securityManager.Login(providerUserId, createPersistentCookie);
}
public static void CreateOrUpdateAccount(string openAuthProvider, string openAuthId, string userName)
{
var user = UserRepository.FindByName(userName);
if (user == null)
{
throw new MembershipUserNotFoundException();
}
var userOAuthAccount = UserOAuthAccountRepository.Find(openAuthProvider, openAuthId);
if (userOAuthAccount == null)
{
UserOAuthAccountRepository.InsertOrUpdate(new UserOAuthAccount
{
OAuthProvider = openAuthProvider,
OAuthId = openAuthId,
UserId = user.Id
});
}
else
{
userOAuthAccount.UserId = user.Id;
}
UserOAuthAccountRepository.Save();
}
}
答案 5 :(得分:0)
以下链接向您展示了如何构建ASP.NET MVC 5 Web应用程序,该应用程序使用户能够使用来自外部身份验证提供程序(如Facebook,Twitter,LinkedIn,Microsoft或Google)的凭据使用OAuth 2.0登录。为简单起见,本教程重点介绍如何使用Facebook和Google现有帐户的凭据。
参考:
MVC 5 App with Facebook, Twitter, LinkedIn and Google OAuth2 Sign-on (C#)