OpenID 与表单身份验证






4.96/5 (70投票s)
演示如何将 OpenID 与 ASP.NET MVC 表单身份验证结合使用。
引言
正如你们中有些人可能从我过去的某些文章/博客中了解到,我并不擅长 Web 开发,但很久以前我有一个想法,想创建一个必须基于 Web 的工具,所以我一直在业余时间忙于构建这个 ASP.NET MVC 网站。
它仍在进行中,但该网站需要的一个领域是登录/身份验证,这在网站上是一个相当普遍的要求。事实上,ASP.NET 早已提供表单身份验证来实现此目的(以及其他选项)。问题是,我真的不想在我的数据库中存储用户名/密码;然后我读到了一个名为 OpenID 的协议,这是一个许多网站已经遵守的协议。如果你有一个符合 OpenID 的登录,我的网站就可以允许其用户直接使用这些凭据,并通过符合 OpenID 的网站进行验证。它们基本上处理登录/身份验证过程,并将用户重定向回原始调用网站(我的网站)。
这听起来可能有点疯狂,但很有可能你自己已经拥有一个 OpenID 登录,因为市面上有许多符合 OpenID 的网站。所以,让用户简单地使用他们现有的登录凭据,而不是让他们为我的网站创建更多凭据,似乎是合理的。
这一切都很好,那么这篇文章是关于什么的呢?很简单,这篇文章将演示如何在 ASP.NET MVC 网站中将 OpenID 与表单身份验证(用于存储身份验证 cookie)结合使用。
我应该提到,这个演示应用程序的网站不是最漂亮的网站,因为我几乎没有对其进行任何样式设计;我故意将其保留原样,以免弄混视线。所以请注意,它不会赢得任何选美比赛。
关于 OpenID 的简要讨论
OpenID 是一种 开放标准,它描述了用户如何在去中心化的方式下 进行身份验证,从而无需 服务 提供自己的 临时 系统,并允许用户整合他们的 数字身份。
OpenID 协议不依赖于中央机构来验证用户的身份。此外,服务或 OpenID 标准都可能不强制要求特定的身份验证用户的方式,允许采用从常见(如密码)到新颖(如 智能卡 或 生物识别)的各种方法。
术语 OpenID 也可能指 OpenID 标准中指定的 ID;这些 ID 的形式是一个唯一的 URL,并由某个处理身份验证的“OpenID 提供商”管理。
OpenID 身份验证现在已被多家大型网站使用和提供。提供商包括 AOL、BBC、Facebook、Google、IBM、MySpace、Orange、PayPal、VeriSign、LiveJournal、Yandex、Ustream 和 Yahoo!
使用 OpenID
OpenID 使用的基本术语词汇表
- 终端用户
- 标识符或 OpenID
- 身份提供商或 OpenID 提供商
- 依赖方
- 用户代理
希望声明特定身份的实体。
专门注册 OpenID URL 或 XRI 并提供 OpenID 身份验证(以及可能的其他身份服务)的服务。请注意,OpenID 规范使用术语“OpenID 提供商”或“OP”。另请参阅:OpenID 提供商列表。
希望验证终端用户标识符的网站;其他术语包括“服务提供商”或现已废弃的“消费者”。
终端用户访问 OpenID 提供商或依赖方时使用的程序(例如浏览器)。
登录
终端用户与依赖方(例如网站)进行交互,依赖方提供一种指定 OpenID 以进行身份验证的方式;终端用户通常已事先与 OpenID 提供商(例如 openid.example.org)注册了 OpenID(例如 alice.openid.example.org)。
依赖方通常将 OpenID 转换为规范 URL 形式(例如 http://alice.openid.example.org/)。
- 使用 OpenID 1.0,依赖方会请求由 URL 标识的 HTML 资源,并读取 HTML 链接标签以发现 OpenID 提供商的 URL(例如 http://openid.example.org/openid-auth.php)。依赖方还会发现是否应使用委托身份(见下文)。
- 使用 OpenID 2.0,客户端通过请求XRDS 文档(也称为Yadis 文档)以及内容类型 application/xrds+xml 来发现 OpenID 提供商 URL;该文档可以位于目标 URL,并且始终可用于目标 XRI。
依赖方可以通过两种模式与 OpenID 提供商通信
checkid_immediate
,在此模式下,依赖方请求 OpenID 提供商不要与终端用户进行交互。所有通信都通过终端用户的用户代理进行转发,而不会明确通知终端用户。checkid_setup
,在此模式下,终端用户通过用于访问依赖方的相同用户代理与 OpenID 提供商进行通信。
checkid_setup
模式在 Web 上更受欢迎;此外,如果操作无法自动化,checkid_immediate
模式可以回退到 checkid_setup
模式。
首先,依赖方和 OpenID 提供商(可选)建立一个共享密钥,并由一个关联句柄引用,然后依赖方会存储该句柄。如果使用 checkid_setup
模式,依赖方会将用户的用户代理重定向到 OpenID 提供商,以便终端用户可以直接与 OpenID 提供商进行身份验证。
身份验证的方法可能不同,但通常,OpenID 提供商会提示终端用户输入密码或InfoCard,然后询问终端用户是否信任依赖方接收必要的身份详细信息。
如果终端用户拒绝 OpenID 提供商信任依赖方的请求,则用户代理将被重定向到依赖方,并显示一条消息,指示身份验证被拒绝;依赖方随后拒绝验证终端用户。
如果终端用户接受 OpenID 提供商信任依赖方的请求,则用户代理将与终端用户的凭据一起重定向到依赖方。然后,该依赖方必须确认凭据确实来自 OpenID 提供商。如果依赖方和 OpenID 提供商先前已建立共享密钥,则依赖方可以通过将其存储的共享密钥与随终端用户凭据一起收到的共享密钥进行比较来验证 OpenID 提供商的身份;这样的依赖方被称为有状态,因为它会在会话之间存储共享密钥。相反,一个无状态或哑依赖方必须进行一次额外的后台请求(check_authentication
)以确保数据确实来自 OpenID 提供商。
在 OpenID 验证完成后,身份验证即被视为成功,终端用户被视为在依赖方上以给定 OpenID(例如 alice.openid.example.org)指定的身份登录。依赖方通常会将其存储终端用户的 OpenID 以及其他会话信息。
如果 OpenID 提供商使用强身份验证,OpenID 可用于安全交易,例如银行业和电子商务。
http://en.wikipedia.org/wiki/OpenID:更新于 2010 年 12 月 9 日。
演示应用程序
正如我一开始所说,演示应用程序是一个 ASP MVC 网站,它展示了如何将 OpenID 与表单身份验证结合使用来存储身份验证 cookie。
从结构上看,它是这样的
可以看到,它遵循标准的 ASP MVC 项目结构,唯一值得注意的其他方面是有几个视图,但只有 Site.aspx 需要在查看之前进行授权。
演示应用程序的目标
我希望我的网站能够使用 OpenID,并且还希望 OpenID 提供商处理实际的登录部分,你知道用户名和密码实际输入的地方。我不想处理这些。OpenID 也允许你让用户在我的网站上输入他们的 OpenID 用户名和密码,然后与 OpenID 提供商进行验证,但这并非我所愿;相反,我希望重定向到一个 OpenID 提供商,让用户登录,然后被告知,是的,该用户是有效的,这是他们的登录令牌,我可以稍后存储。
这可能不适合你的目的,在这种情况下,你应该寻找替代解决方案。
演示应用程序展示
当我们启动演示代码时,在 Index.aspx(ASP.NET MVC 默认的 HomeController.Index 操作确保我们最终会看到 Index.aspx 视图)中,我们将看到以下内容。此页面不要求任何形式的授权。
它有一个指向一个页面的链接(Site.aspx),该页面在查看之前需要授权;当单击此链接时,我们将看到类似这样的内容
那里有几点需要注意,例如
- 浏览器的地址栏显示一个查询字符串,其中包含一个
ReturnUrl
,它被设置为 ReturnUrl=/Site/Data,这恰好是我们尝试加载的需要授权才能查看的页面的控制器/操作 URL。这个ReturnUrl
查询字符串参数是表单身份验证的标准功能,我们将最终使用它来存储身份验证 cookie。 - 有很多图像按钮可以点击。这些图像中的每一个都代表一个符合 OpenID 的网站,你可以用它来登录。例如,我有一个 Google 账户,所以我可以选择使用我的 Google 凭据。我应该指出,我从某处博客获取了 Logon.aspx 页面的大部分内容,但我记不清是从哪里来的,所以很抱歉没有在这篇文章中直接提及来源。
如果我继续使用我的 Google 账户,点击 Google 图像,当前的浏览器会话将被导航到 Google,在那里我可以输入我的常规登录凭据,如下所示
一旦我输入了我通常的 Google(符合 OpenID)凭据,我就会被返回到我离开 OpenID 兼容网站进行登录之前的页面,在我这里显然是 Google。
我在使用我的 Google 凭据登录后,下面显示了这一点
从这里,你可能可以看到 URL 不是我最初想查看的页面,也不是 Site/Data,也不是登录页面。这就是本文代码的全部内容;它向你展示了如何处理登录到符合 OpenID 的网站,以及如何使用表单身份验证来管理身份验证 cookie。在上面的示例中,我们基本上做了这个
- 加载一个不需要任何身份验证的页面(HomeController -> Index 操作 -> Index.aspx)。
- 从那里,我们点击了一个需要身份验证的页面链接(SiteController -> Data 操作 -> Site.aspx)。这立即意识到我们尚未通过身份验证,由于缺少表单身份验证 cookie,并将浏览器会话重定向到由 AccountController 管理的 Login.aspx 页面。
- 从 Login.aspx 页面,我们有一个
ReturnUrl
(设置为 ReturnUrl=/Site/Data)指向用户尝试查看但需要用户进行身份验证才能返回的原始页面。 - 接下来,我们选择了一个 OpenID 提供商;然后我们被重定向到 OpenID 提供商网站,在那里我们登录。
- 如果登录过程成功,我们将被重定向回用户想要查看的原始页面(这就是表单身份验证为我们提供的
ReturnUrl
),直到用户通过身份验证才有可能实现。这是通过表单身份验证 cookie 和 ASP.NET MVC 属性实现的,我们将在下一节中讨论。
那么它是如何工作的呢?
在我开始解释之前,让我先说一句,我的代码依赖于一个第三方 DLL,名为“DotNetOpenAuth.dll”,它是免费提供的,网址是:http://www.dotnetopenauth.net/,那里有许多基于 .NET 的演示示例,当然还有 DotNetOpenAuth.dll 本身。
指定页面在查看前需要授权
这也许是提供的演示代码中最简单的部分。幸运的是,ASP.NET MVC 附带了一个非常方便的 AuthoriseAttribute
,它可以直接应用于你的控制器,从而限制对控制器操作的访问,除非用户已通过身份验证。在我们的情况下,这意味着我们已经登录并存储了会话中的表单身份验证 cookie。
这就是演示代码中需要授权才能在用户查看调用任何操作的结果之前执行的控制器样子
using System.Web.Mvc;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.RelyingParty;
namespace DotNetOpenIdTest.Controllers
{
[HandleError]
[Authorize]
public class SiteController : Controller
{
public ActionResult Data()
{
return View("Site");
}
}
}
登录页面
登录页面是显示所有 OpenID 提供商的地方,用户可以在那里点击其中一个来重定向到 OpenID 提供商的网站。如我所说,我从某处获取了此页面的大部分内容,但我就是记不起来了,所以如果你认为你知道它来自哪里,请告诉我。总之,登录页面的 View 看起来像这样
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>OpenId Demo</title>
<link rel="stylesheet" type="text/css"
media="screen" href="../../Content/openid.css" />
<script type="text/javascript" src="../../Scripts/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="../../Scripts/jquery.openid.js"></script>
<script type="text/javascript">
$(function () { $("form.openid:eq(0)").openid(); });
</script>
</head>
<body>
<div>
<h2>
Logon Using OpenId</h2>
<p>
<%=ViewData["message"]%></p>
<form class="openid" method="post" action="/Account/Logon">
<div>
<ul class="providers">
<li class="openid" title="OpenID">
<img src="../../Content/images/openidW.png" alt="icon" />
<span><strong>http://{your-openid-url}</strong></span></li>
<li class="direct" title="Google">
<img src="../../Content/images/googleW.png" alt="icon" />
<span>https://www.google.com/accounts/o8/id</span></li>
<li class="direct" title="Yahoo">
<img src="../../Content/images/yahooW.png" alt="icon" />
<span>http://yahoo.com/</span></li>
<li class="username" title="AOL screen name">
<img src="../../Content/images/aolW.png" alt="icon" />
<span>http://openid.aol.com/<strong>username</strong></span></li>
<li class="username" title="MyOpenID user name">
<img src="../../Content/images/myopenid.png" alt="icon" />
<span>http://<strong>username</strong>.myopenid.com/</span></li>
<li class="username" title="Flickr user name">
<img src="../../Content/images/flickr.png" alt="icon" />
<span>http://flickr.com/<strong>username</strong>/</span></li>
<li class="username" title="Technorati user name">
<img src="../../Content/images/technorati.png" alt="icon" />
<span>http://technorati.com/people/technorati/
<strong>username</strong>/</span></li>
<li class="username" title="Wordpress blog name">
<img src="../../Content/images/wordpress.png" alt="icon" />
<span>http://<strong>username</strong>.wordpress.com</span></li>
<li class="username" title="Blogger blog name">
<img src="../../Content/images/blogger.png" alt="icon" />
<span>http://<strong>username</strong>.blogspot.com/</span></li>
<li class="username" title="LiveJournal blog name">
<img src="../../Content/images/livejournal.png" alt="icon" />
<span>http://<strong>username</strong>.livejournal.com</span></li>
<li class="username" title="ClaimID user name">
<img src="../../Content/images/claimid.png" alt="icon" />
<span>http://claimid.com/<strong>username</strong></span></li>
<li class="username" title="Vidoop user name">
<img src="../../Content/images/vidoop.png" alt="icon" />
<span>http://<strong>username</strong>.myvidoop.com/</span></li>
<li class="username" title="Verisign user name">
<img src="../../Content/images/verisign.png" alt="icon" />
<span>http://<strong>username</strong>.pip.verisignlabs.com/</span>
</li>
</ul>
</div>
<fieldset>
<label for="openid_username">
Enter your <span>Provider user name</span></label>
<div>
<span></span>
<input type="text" name="openid_username" /><span></span>
<input type="submit" value="Login" /></div>
</fieldset>
<fieldset>
<label for="openid_identifier">
Enter your <a class="openid_logo" href="http://openid.net">OpenID</a></label>
<div>
<input type="text" name="openid_identifier" />
<input type="submit" value="Login" /></div>
</fieldset>
</form>
</div>
</body>
</html>
以下是基于 jQuery 的 JavaScript(jquery.openid.js)正在将图像连接到 AccountController
的 Logon
操作。真正重要的事情是页面由以下 JavaScript 提交;所有实际工作都在 AccountController
的 Logon
操作代码中完成。
//jQuery OpenID Plugin 1.1
//Copyright 2009 Jarrett Vance http://jvance.com/pages/jQueryOpenIdPlugin.xhtml
$.fn.openid = function() {
var $this = $(this);
var $usr = $this.find('input[name=openid_username]');
var $id = $this.find('input[name=openid_identifier]');
var $front = $this.find('div:has(input[name=openid_username])>span:eq(0)');
var $end = $this.find('div:has(input[name=openid_username])>span:eq(1)');
var $usrfs = $this.find('fieldset:has(input[name=openid_username])');
var $idfs = $this.find('fieldset:has(input[name=openid_identifier])');
var submitusr = function() {
if ($usr.val().length < 1) {
$usr.focus();
return false;
}
$id.val($front.text() + $usr.val() + $end.text());
return true;
};
var submitid = function() {
if ($id.val().length < 1) {
$id.focus();
return false;
}
return true;
};
var direct = function() {
var $li = $(this);
$li.parent().find('li').removeClass('highlight');
$li.addClass('highlight');
$usrfs.fadeOut();
$idfs.fadeOut();
$this.unbind('submit').submit(function() {
$id.val($this.find("li.highlight span").text());
});
$this.submit();
return false;
};
var openid = function() {
var $li = $(this);
$li.parent().find('li').removeClass('highlight');
$li.addClass('highlight');
$usrfs.hide();
$idfs.show();
$id.focus();
$this.unbind('submit').submit(submitid);
return false;
};
var username = function() {
var $li = $(this);
$li.parent().find('li').removeClass('highlight');
$li.addClass('highlight');
$idfs.hide();
$usrfs.show();
$this.find('label[for=openid_username] span').text($li.attr("title"));
$front.text($li.find("span").text().split("username")[0]);
$end.text("").text($li.find("span").text().split("username")[1]);
$id.focus();
$this.unbind('submit').submit(submitusr);
return false;
};
$this.find('li.direct').click(direct);
$this.find('li.openid').click(openid);
$this.find('li.username').click(username);
$id.keypress(function(e) {
if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
return submitid();
}
});
$usr.keypress(function(e) {
if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
return submitusr();
}
});
$this.find('li span').hide();
$this.find('li').css('line-height', 0).css('cursor', 'pointer');
$this.find('li:eq(0)').click();
return this;
};
登录过程
登录过程在 AccountController
中进行,工作流程如下
- 请求需要授权的页面。
- 如果用户未通过身份验证,则重定向到 GET
AccountController
Logon
操作。 - 显示 Logon 视图,该视图包含所有 OpenID 提供商链接,HTML 表单设置为 POST 到
AccountController
POSTLogon
操作。 - 用户选择一个 OpenID 提供商并点击它,这将调用 JavaScript,实际上只是存储 OpenID 提供商字符串并向
AccountController
Logon
操作发出 POST 请求。 AccountController
的 POSTLogon
操作执行两项操作- 它会添加一个
ClaimRequest
,询问 OpenID 提供商在成功响应 OpenID 提供商时包含的数据中包含电子邮件/全名。 - 然后重定向到 OpenID 提供商网站(通过 DotNetOpenAuth.dll 的魔法),用户在那里输入他们的详细信息。
- 如果用户在 OpenID 提供商网站上输入了有效的凭据,他们将被重定向到
AccountController
(通过 DotNetOpenAuth.dll 的魔法)上的默认Logon
操作,此时,它将检查 OpenID 提供商IAuthenticationResponse response
的响应,该响应可以通过调用OpenIdRelyingParty
类型的GetResponse()
方法获得。如果响应被发现为AuthenticationStatus.Authenticated
,则用户被视为已验证,然后可以从 OpenID 提供商的响应中请求有关用户的更多详细信息,这是通过response.GetUntrustedExtension<ClaimsResponse>
/response.GetExtension<ClaimsResponse>
完成的,其中ClaimsResponse
是与我们在AccountController
的 POSTLogon
操作(步骤 5)中要求 OpenID 提供商包含的ClaimsResponse
相匹配的响应。然后,我们可以使用ClaimsResponse
从 OpenID 提供商的ClaimsResponse
中获取用户的电子邮件和全名,这些信息存储在一个我创建的小数据结构中,称为UserData
,如下所示 - 最后一步是为 OpenID 身份验证的
UserData
对象创建FormsAuthenticationTicket/HttpCookie
。
public class UserData
{
public string Email { get; set; }
public string FullName { get; set; }
public override string ToString()
{
return String.Format("{0}-{1}", Email, FullName);
}
}
这是 AccountController
的所有代码
using System.Web.Mvc;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.RelyingParty;
using System.Web.Security;
using System.Linq;
using System;
using System.Text.RegularExpressions;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
using System.Web;
namespace DotNetOpenIdTest.Controllers
{
/// <summary>
/// Simple class that represents a OpenId validated user in the system
/// </summary>
public class UserData
{
public string Email { get; set; }
public string FullName { get; set; }
public override string ToString()
{
return String.Format("{0}-{1}", Email, FullName);
}
}
/// <summary>
/// Provides actions/methods for creating an OpenId request and how to
/// obtain information from a authenticated OpenId response, and also
/// has methods for creating a Forms Authentication Ticket
/// </summary>
public class AccountController : Controller
{
#region Actions
/// <summary>
/// Show default logon page, and also see if user is already authenticated.
/// If they are we can examine the IAuthenticationResponse to get the
/// user data from the OpenId provider
/// </summary>
/// <returns></returns>
public ActionResult LogOn()
{
ViewData["message"] = "You are not logged in";
OpenIdRelyingParty openid = new OpenIdRelyingParty();
IAuthenticationResponse response = openid.GetResponse();
//check for ReturnUrl, which we should have if we use forms
//authentication and [Authorise] on our controllers
if (Request.Params["ReturnUrl"] != null)
Session["ReturnUrl"] = Request.Params["ReturnUrl"];
if (response != null &&
response.Status == AuthenticationStatus.Authenticated)
{
var claimUntrusted =
response.GetUntrustedExtension<ClaimsResponse>();
var claim = response.GetExtension<ClaimsResponse>();
UserData userData = null;
if (claim != null)
{
userData = new UserData();
userData.Email = claim.Email;
userData.FullName = claim.FullName;
}
//fallback to claim untrusted, as some OpenId providers may not
//provide the trusted ClaimsResponse, so we have to fallback to
//trying the untrusted on
if (claimUntrusted != null && userData == null)
{
userData = new UserData();
userData.Email = claimUntrusted.Email;
userData.FullName = claimUntrusted.FullName;
}
//now store Forms Authorisation cookie
IssueAuthTicket(userData, true);
//store ClaimedIdentifier it in Session
//(this would more than likely be something
//you would store in a database I guess
Session["ClaimedIdentifierMessage"] = response.ClaimedIdentifier;
//If we have a ReturnUrl we MUST be using forms authentication,
//so redirect to the original ReturnUrl
if (Session["ReturnUrl"] != null)
{
string url = Session["ReturnUrl"].ToString();
return new RedirectResult(url);
}
//This should not happen if all
//controllers have [Authorise] used on them
else
throw new InvalidOperationException("There is no ReturnUrl");
}
return View("LogOn");
}
/// <summary>
/// Logon post request, so redirect to OpenID
/// provider and when they authenticate,
/// redirect back to orginal url which is the
/// one that needed authenticating in the 1st place
/// </summary>
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(string openid_identifier)
{
var openid = new OpenIdRelyingParty();
IAuthenticationRequest request =
openid.CreateRequest(Identifier.Parse(openid_identifier));
var fields = new ClaimsRequest();
fields.Email = DemandLevel.Require;
fields.FullName = DemandLevel.Require;
request.AddExtension(fields);
return request.RedirectingResponse.AsActionResult();
}
/// <summary>
/// Logoff and clears forms authentication cookie
/// </summary>
/// <returns></returns>
public ActionResult LogOff()
{
Session.RemoveAll();
Session.Clear();
Session.Abandon();
Response.Cookies.Remove("AUTHCOOKIE");
FormsAuthentication.SignOut();
return View("LogOff");
}
#endregion
#region Private Methods
/// <summary>
/// Issue forms authentication ticket
/// for authenticated user, and store the cookie
/// </summary>
private void IssueAuthTicket(UserData userData, bool rememberMe)
{
FormsAuthenticationTicket ticket =
new FormsAuthenticationTicket(1, userData.Email,
DateTime.Now, DateTime.Now.AddDays(10),
rememberMe, userData.ToString());
string ticketString = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie =
new HttpCookie(FormsAuthentication.FormsCookieName, ticketString);
if (rememberMe)
cookie.Expires = DateTime.Now.AddDays(10);
HttpContext.Response.Cookies.Add(cookie);
}
#endregion
}
}
Web.Config
我发现我必须在 Web.Config 中为 OpenId.Dll 添加一个 dotNetOpenAuth
配置节,以便与某些提供商(Google)正确工作。哦,我还显示了表单身份验证已启用。
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="dotNetOpenAuth"
type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection"
requirePermission="false"
allowLocation="true"/>
</configSections>
.......
.......
.......
.......
.......
<!-- The following OPTIONAL behavior allows
RPs to use SREG only, but be compatible
with OPs that use Attribute Exchange (in various formats). -->
<dotNetOpenAuth>
<openid>
<relyingParty>
<behaviors>
<add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform,
DotNetOpenAuth" />
</behaviors>
</relyingParty>
</openid>
</dotNetOpenAuth>
<!-- Allow Forms Authentication,
and set Login url (AccountController/LogOn action) -->
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880"/>
</authentication>
.......
.......
.......
.......
</configuration>
就这些
总之,这就是我真正想说的。我知道 Web 开发不是我的常规领域,所以我很可能犯了一些低级错误。如果属实,请告诉我,因为我正要将它应用到我的私人业余时间 ASP MVC 项目中。所以,任何正在阅读此文的 ASP MVC 高级/资深开发人员,如果看到什么错误,请告诉我。
同样,如果你喜欢这篇文章,通过评论/投票让我知道会很好。