在MVC3应用程序中使用OpenID / OpenAuth和重写的身份验证方法

时间:2012-10-30 18:44:06

标签: asp.net-mvc asp.net-mvc-3 oauth openid dotnetopenauth

我们通过使用用户凭据调用webservice并返回包含用户ID的WCF结构(“LogonTicket”)来覆盖MVC3应用程序中的基本身份验证。此LogonTicket用于“对每次对Web服务进行的调用对用户进行身份验证。

现在,我们通过替换Web.config中的defaultProvider来覆盖。我们在这个被覆盖的提供者所做的就是 覆盖ValidateUser()函数。这就是我们用他们的凭据和返回来调用Web服务的地方 “LogonTicket”。

这是我们AccountController的LogOn()函数,基本上是模板的基本代码:

public ActionResult LogOn(LogOnModel model)
{
    string ReturnUrl = "";
    if (HttpContext.Request.UrlReferrer.Query.Length > 11)
    {
        ReturnUrl = Uri.UnescapeDataString(HttpContext.Request.UrlReferrer.Query.Substring(11));
    }
    if (ModelState.IsValid)
    {
        if (Membership.ValidateUser(model.UserName, model.Password))
        {
            FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
            if (Url.IsLocalUrl(ReturnUrl) && ReturnUrl.Length > 1 && ReturnUrl.StartsWith("/")
                && !ReturnUrl.StartsWith("//") && !ReturnUrl.StartsWith("/\\"))
            {
                return Redirect(ReturnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }
        else
        {
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
        }
    }

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

这是来自新默认提供程序的重写的ValidateUser()函数:

public override bool ValidateUser(string username, string password)
{
    MyServiceClient mps = new MyServiceClient();
    string sha1password = HashCode(password);
    LogonInfo logonInfo = mps.GetLogonTicket(username, sha1password);
    if (logonInfo.LogonTicket != "" && logonInfo.LogonTicket != "0") 
    {
    // Authenticated so set session variables
        HttpContext.Current.Session["LogonTicket"] = logonInfo.LogonTicket;
        HttpContext.Current.Session["ParticipantID"] = logonInfo.ParticipantID;
        return true;
    }
    else
    {
        return false;
    }
}

我不确定如何结合使用这两者,所以我的问题是:

  1. 如何实施OpenID和Facebook登录并保留我当前的身份验证方法?
  2. 我们如何使用当前用户数据库值“映射”OpenID用户?我们必须知道所以我们可以检索他们的信息。  我知道我们可以检索他们的电子邮件地址,但是如果他们的OpenID电子邮件与他们在我们网站上的记录使用的电子邮件不同呢?
  3. 在任何地方都有任何关于如何做到这一点的例子吗?
  4. 感谢您查看我的问题。

2 个答案:

答案 0 :(得分:3)

我做了一个需要多种登录可能性的项目(自定义帐户,Google和Facebook)

最后,使用ASP.NET进行身份验证完全取决于您的配置。 (在您的情况下,它是FormsAuthentication)这意味着FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);基本上确定了与您的用户有关的所有内容以及您设置的位置不受限制。

您现在基本上与我们开始时的实现相同,使用MembershipProvider来处理您自己的自定义帐户。您现在只需要扩展以方便openIds。您必须针对每种登录类型展开Controller各种操作(现在您可以添加ActionResult LogOn(),例如:ActionResult LogOnOpenId())。在该方法中,您基本上调用相同的代码,而不是Membership.ValidateUser(model.UserName, model.Password),而是调用OpenId服务。

我在下面提供了使用dotnetopenauth进行谷歌实施的示例。服务方法使用formsService.SignIn(userId.Value.ToString(), false);,它基本上调用FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);(我们只针对SecurityPrincipal执行一些自定义行为,但这不会影响您的身份验证过程)。您还可以看到我们在收到新用户时创建了一个新帐户。为了解决您的问题第2部分,我们已经实现了一个可以合并的配置文件,如果您可以提供另一个登录。这允许我们的用户保持他们的帐户合并并使用他们喜欢的任何登录方法。

关于多个登录的示例,我将参考Tomas的答案,他们将StackExchange作为一个很好的例子。另外我建议你安装MVC4和VS2012,然后做一个文件>新项目。最新的MVC默认模板包括openid实现和自定义登录!

示例google openid实施:

控制器方法:

    public virtual ActionResult LoginGoogle(string returnUrl, string runAction)
    {
        using (var openId = new OpenIdRelyingParty())
        {
            IAuthenticationResponse response = openId.GetResponse();

            // If we have no response, start 
            if (response == null)
            {
                // Create a request and redirect the user 
                IAuthenticationRequest req = openId.CreateRequest(WellKnownProviders.Google);
                var fetch = new FetchRequest();
                fetch.Attributes.AddRequired(WellKnownAttributes.Name.First);
                fetch.Attributes.AddRequired(WellKnownAttributes.Name.Last);
                fetch.Attributes.AddRequired(WellKnownAttributes.Contact.Email);
                fetch.Attributes.AddRequired(WellKnownAttributes.Preferences.Language);
                req.AddExtension(fetch);

                req.RedirectToProvider();

                return null;
            }

            _service.ConnectViaGoogle(response, TempData);
    }

服务方法:

    public void ConnectViaGoogle(IAuthenticationResponse response, TempDataDictionary tempData)
    {
        // We got a response - check it's valid and that it's me 
        if (response.Status == AuthenticationStatus.Authenticated)
        {
            var claim = response.GetExtension<FetchResponse>();
            Identifier googleUserId = response.ClaimedIdentifier;
            string email = string.Empty;
            string firstName = string.Empty;
            string lastName = string.Empty;
            string language = string.Empty;

            if (claim != null)
            {
                email = claim.GetAttributeValue(WellKnownAttributes.Contact.Email);
                firstName = claim.GetAttributeValue(WellKnownAttributes.Name.First);
                lastName = claim.GetAttributeValue(WellKnownAttributes.Name.Last);
                language = claim.GetAttributeValue(WellKnownAttributes.Preferences.Language);
            }

            //Search User with google UserId
            int? userId = _userBL.GetUserIdByGoogleSingleSignOnId(googleUserId);

            //if not exists -> Create
            if (!userId.HasValue)
            {
                _userBL.CreateGoogleUser(
                    googleUserId,
                    firstName,
                    lastName,
                    email,
                    language,
                    DBConstants.UserStatus.DefaultStatusId,
                    out userId);
            }

            if (userId.HasValue)
            {
                _userBL.UpdateLastLogon(userId.Value);
                var formsService = new FormsAuthenticationService();
                formsService.SignIn(userId.Value.ToString(), false);
                AfterLoginActions(tempData);
            }
        }
    }

有任何问题或意见吗?我很乐意听到他们的声音。

答案 1 :(得分:1)

  1. 应该完全可以使用多种身份验证方法。所有IIS / ASP.net都关心的是FormsAuthentication cookie。因此,您将为标准用户名/密码auth设置一组操作,为OpenId设置另一组操作。这至少是我在一个项目上所做的。
  2. 您甚至不能信任openId提供商给您一个电子邮件地址!该问题的常见解决方案是允许用户在登录后将多个OpenId标识符(URI)附加到他的帐户。这例如是StackOverflow如何工作。如果这是用户第一次访问系统,则您可以自动创建新帐户,或强制用户完成注册过程。
    当我在上述系统中添加OpenId支持时,它有一个用于存储用户名和密码的现有表(users表)。我添加了一个与users表有多对一关系的新表,并使用它来存储URI。
  3. 如上所述,StackOverflow本身就是一个很好的起点,http://www.dotnetopenauth.net/项目中也有很多很好的例子。 据我所知,SO的来源不公开,他们正在使用dotnetopenauth项目 这可能是抽象的,但是这个库对于开源果园CMS来说是一个openId(以及其他内容):http://orchardopenauth.codeplex.com/
  4. 我希望这会有所帮助,但如果您有任何疑问,请详细说明您的问题。