Stack Overflow实际使用的是哪种OpenID解决方案?

11

我知道有关于这个问题的其他问答,但它们已经过时了,而且我无法在任何地方找到可靠的答案。

Stack Overflow到底使用什么来验证用户身份?网站DotNetOpenAuth声称它是用来验证用户身份的。但对我来说,最(外观)相似的是OAuth C# Library

那么它真正使用的是什么?或者,我该如何模仿同样的用户界面?

我想使用ASP.NET MVC创建完全相同的OpenID认证。


我觉得这个问题需要特殊的评论。自从它被提出来后,它已经在StackOverflow元站点上移动,然后回到主站点,再到元站点,最后又回到主站点。感觉它既属于这两个站点,也可能都不属于?我认为它属于StackOverflow主站点,因为我最初是在那里发布的。这是因为我提问并不是因为对SO网站本身的好奇,而是因为我想使用相同的解决方案、相同的技术。因此,Lirick的答案是我的问题的好答案,但这样的答案与元站点无关(太技术性),所以它留在这里。 - Rasto
2个回答

17

StackOverflow使用DotNetOpenAuth

来自博客:

但幸运的是,我们与Andrew Arnott有良好的对话。他是我们所使用的开源DotNetOpenAuth库的主要作者。


15
我能够在我的网站(www.mydevarmy.com)上使用DotNetOpenAuth进行OpenID认证,而且花费的时间相对较短(需要注意的是,我对ASP.NET、MVC、DotNetOpenAuth等完全不熟悉)。 DotNetOpenAuth附带各种示例,甚至还有一个ASP.NET MVC示例,但该示例只提供了视图和控制器,实际上没有模型,而模型是MVC中的M : )。因此,我在SO上提出了以下问题: 简单登录的MVC模式中各组件的职责是什么 那么,在MVC中,一个非常简单的OpenID登录应该是什么样子呢?好的,让我们来看看... 1. 您将需要一个模型:
public class User
{
    [DisplayName("User ID")]
    public int UserID{ get; set; }

    [Required]
    [DisplayName("OpenID")]
    public string OpenID { get; set; }
}

public class FormsAuthenticationService : IFormsAuthenticationService
{
    public void SignIn(string openID, bool createPersistentCookie)
    {
        if (String.IsNullOrEmpty(openID)) throw new ArgumentException("OpenID cannot be null or empty.", "OpenID");

        FormsAuthentication.SetAuthCookie(openID, createPersistentCookie);
    }

    public void SignOut()
    {
        FormsAuthentication.SignOut();
    }
}  

2. 您需要一个控制器:

[HandleError]
public class UserController : Controller
{
    private static OpenIdRelyingParty openid = new OpenIdRelyingParty();
    public IFormsAuthenticationService FormsService { get; set; }

    protected override void Initialize(RequestContext requestContext)
    {
        if (FormsService == null) 
        {
            FormsService = new FormsAuthenticationService(); 
        }

        base.Initialize(requestContext);
    }

    // **************************************
    // URL: /User/LogIn
    // **************************************
    public ActionResult LogIn()
    {
        if (User.Identity.IsAuthenticated)
        {
            return RedirectToAction("Profile", "User");
        }

        Identifier openID;
        if (Identifier.TryParse(Request.QueryString["dnoa.userSuppliedIdentifier"], out openID))
        {
            return LogIn(new User { OpenID = openID }, Request.QueryString["ReturnUrl"]);
        }
        else
        {
            return View();
        }
    }

    [HttpPost]
    public ActionResult LogIn(User model, string returnUrl)
    {
        string openID = ModelState.IsValid?model.OpenID:Request.Form["openid_identifier"];

        if (User.Identity.IsAuthenticated)
        {
            return RedirectToAction("Profile", "User");
        }
        else if (!string.IsNullOrEmpty(openID))
        {
            return Authenticate(openID, returnUrl);
        }
        else if(ModelState.IsValid)
        {
            ModelState.AddModelError("error", "The OpenID field is required.");
        }

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

    // **************************************
    // URL: /User/LogOut
    // **************************************
    public ActionResult LogOut()
    {
        if (User.Identity.IsAuthenticated)
        {
            FormsService.SignOut();
        }

        return RedirectToAction("Index", "Home");
    }

    // **************************************
    // URL: /User/Profile
    // **************************************
    [Authorize]
    public ActionResult Profile(User model)
    {
        if (User.Identity.IsAuthenticated)
        {
            // ------- YOU CAN SKIP THIS SECTION ----------------
            model = /*some code to get the user from the repository*/;

            // If the user wasn't located in the database
            // then add the user to our database of users
            if (model == null)
            {
                model = RegisterNewUser(User.Identity.Name);
            }
            // --------------------------------------------------

            return View(model);
        }
        else
        {
            return RedirectToAction("LogIn");
        }
    }

    private User RegisterNewUser(string openID)
    {
        User user = new User{OpenID = openID};

        // Create a new user model

        // Submit the user to the database repository

        // Update the user model in order to get the UserID, 
        // which is automatically generated from the DB.
        // (you can use LINQ-to-SQL to map your model to the DB)

        return user;
    }

    [ValidateInput(false)]
    private ActionResult Authenticate(string openID, string returnUrl)
    {
        var response = openid.GetResponse();
        if (response == null)
        {
            // Stage 2: user submitting Identifier
            Identifier id;
            if (Identifier.TryParse(openID, out id))
            {
                try
                {
                    return openid.CreateRequest(openID).RedirectingResponse.AsActionResult();
                }
                catch (ProtocolException ex)
                {
                    ModelState.AddModelError("error", "Invalid OpenID.");

                    ModelState.AddModelError("error", ex.Message);
                    return View("LogIn");
                }
            }
            else
            {
                ModelState.AddModelError("error", "Invalid OpenID.");
                return View("LogIn");
            }
        }
        else
        {
            // Stage 3: OpenID Provider sending assertion response
            switch (response.Status)
            {
                case AuthenticationStatus.Authenticated:
                    Session["FriendlyIdentifier"] = response.FriendlyIdentifierForDisplay;
                    FormsAuthentication.SetAuthCookie(response.FriendlyIdentifierForDisplay, true);
                    if (!string.IsNullOrEmpty(returnUrl))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Profile", "User");
                    }
                case AuthenticationStatus.Canceled:
                    ModelState.AddModelError("error", "Authentication canceled at provider.");
                    return View("LogIn");
                case AuthenticationStatus.Failed:
                    ModelState.AddModelError("error", "Authentication failed: " + response.Exception.Message);
                    return View("LogIn");
            }
        }
        return new EmptyResult();
    }
}

3. 您需要一个视图:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<YourProject.Models.User>" %>

<asp:Content ID="loginTitle" ContentPlaceHolderID="TitleContent" runat="server">
    Log in - YourWebSiteName
</asp:Content>
<asp:Content ID="loginContent" ContentPlaceHolderID="MainContent" runat="server">
        <p>
            <%--- If you have a domain, then you should sign up for an affiliate id with MyOpenID or something like that ---%>
            Please log in with your OpenID or <a href="https://www.myopenid.com/signup?affiliate_id=????">create an
                OpenID with myOpenID</a> if you don't have one.
        </p>
        <% 
        string returnURL = HttpUtility.UrlEncode(Request.QueryString["ReturnUrl"]);
        if (returnURL == null)
        {
            returnURL = string.Empty;
        }

        using (Html.BeginForm("LogIn", "User", returnURL))
        {%>
            <%= Html.LabelFor(m => m.OpenID)%>:
            <%= Html.TextBoxFor(m => m.OpenID)%>
            <input type="submit" value="Log in" />
        <% 
        } %>

        <%--- Display Errors ---%>
        <%= Html.ValidationSummary()%>
</asp:Content>

请注意,我没有为您提供Profile视图,但这应该很容易弄清楚。

谢谢你!这是非常好的答案!我会尝试一下,如果它有效的话,你应该把它写到你的博客上,如果你有的话。希望这个回答能得到更多的投票。我必须接受Oded的答案,因为我已经承诺了。 - Rasto
@drasto,没问题...我对在meta和Oded上获得积分并不那么感兴趣,而且Oded已经有更多的赞同票了。我只是希望这对你有所帮助 :) - Kiril
1
@drasto,我写了一篇关于这个的博客文章:http://codesprout.blogspot.com/2011/03/using-dotnetopenauth-to-create-simple.html - Kiril
谢谢。我相信一旦谷歌找到它,这对很多人都会有用。 - Rasto

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接