如何使用ASP.NET中MVC4唯一标识符的URL OpenID提供 [英] How to use OpenID providers with unique identifier URLs in ASP.NET MVC4
问题描述
新的 SimpleMembershipProvider
在ASP.NET MVC4可以方便,内置支持两个流行的OpenID提供商(谷歌和雅虎)和三个OAuth的供应商(微软,Facebook的实施, 推特)。
The new SimpleMembershipProvider
implemented in ASP.NET MVC4 allows easy, built-in support for two popular OpenID providers (Google and Yahoo) and three OAuth providers (Microsoft, Facebook, Twitter).
在<一个实现的供应商href=\"https://github.com/DotNetOpenAuth/DotNetOpenAuth/tree/master/src/DotNetOpenAuth.AspNet/Clients\"><$c$c>DotNetOpenAuth.AspNet.Clients$c$c>与使用 SimpleMembershipProvider
都使用静态网址为他们的身份服务---那就是,所有用户都使用同一个众所周知的URL来访问供应商。用户的OpenID的标识符与用于访问该身份服务的URL分离
The providers implemented in DotNetOpenAuth.AspNet.Clients
for use with the SimpleMembershipProvider
all use static URLs for their identity services --- that is, all users use the same well-known URL to access the provider. The users' OpenID identifiers are separate from the URL used to access the identity service.
例如,谷歌的OpenID服务URL是 https://www.google.com/accounts/o8/id
为所有用户。
For example, Google's OpenID service URL is https://www.google.com/accounts/o8/id
for all users.
这工作原理与 SimpleMembershipProvider
在MVC4,这里需要知道,不变,并注册在你的MVC应用程序启动时的身份提供者的网址。
This works with the SimpleMembershipProvider
in MVC4, where the URL of the identity provider needs to be known, constant, and registered at the time your MVC app starts up.
问题是,其他OpenID提供通常使用用户的唯一OpenID识别为URL访问身份服务。
The problem is, other OpenID providers commonly use the user's unique OpenID identifier as the URL to access the identity service.
例如,AOL和Word preSS使用 https://openid.aol.com/ {用户名}
和 HTTPS:/ /{username}.word$p$pss.com
,分别为。
For example, AOL and WordPress use https://openid.aol.com/{username}
and https://{username}.wordpress.com
, respectively.
如果您替换 SimpleMembershipProvider
用自己实现的 ExtendedMembershipProvider
,那么你可以滚你自己提供者实现,但随后不与MVC4 帐户
控制器工作外的开箱。
If you replace the SimpleMembershipProvider
with your own implementation of an ExtendedMembershipProvider
, then you can roll your own provider implementations, but then it doesn’t work with the MVC4 Account
controller out-of-the-box.
怎样才能实现一个新的OpenID的使用依赖方的 SimpleMembershipProvider
,当供应商使用的唯一标识符,在URL中的用户名?
How does one implement a new OpenID Relying Party using the SimpleMembershipProvider
, when the provider uses unique identifiers with the username in the URL?
推荐答案
我已经开发了以下的解决方案,为我的作品,而我在案例分享它会帮助别人,但我真的想看看有没有更直接的方法还是最佳实践说我失踪了。
I’ve developed the following solution that works for me, and I’m sharing in case it will help others, but I would really like to see if there’s a more direct method or "best practice" that I’m missing.
基本上,你需要实现 OpenIdClient
是与初始化的 ProviderIdentifier
有包含关键字的URL __ __名
。
Basically, you need to implement an OpenIdClient
that is initialized with a ProviderIdentifier
that has a URL containing the keyword __username__
.
在运行时,提供程序名称和用户名被传递给帐户
控制器,其中供应商客户是按名称选择,用户名代替 __ __名
之前关键字认证请求被发送到供应商。
At runtime, the provider name and user name are passed to the Account
controller, where the provider client is selected by name, and the user name is substituted for the __username__
keyword before the authentication request is sent to the provider.
贡献的微软DotNetOpenAuth OpenID提供商类继承的基类<一href=\"https://github.com/DotNetOpenAuth/DotNetOpenAuth/blob/master/src/DotNetOpenAuth.AspNet/Clients/OpenID/OpenIDClient.cs\"><$c$c>DotNetOpenAuth.AspNet.Clients.OpenIdClient$c$c>,它实现了OpenID提供商类所需的 IAuthenticationClient
接口。与<起始href=\"https://github.com/DotNetOpenAuth/DotNetOpenAuth/blob/master/src/DotNetOpenAuth.AspNet/Clients/OpenID/GoogleOpenIdClient.cs\">source对于谷歌提供因为它有一个直观的实现,自定义,以使该使用的自定义网址提供商工作的 GenericOpenIdClient
类。
The DotNetOpenAuth OpenID provider classes contributed by Microsoft inherit the base class DotNetOpenAuth.AspNet.Clients.OpenIdClient
, which implements the IAuthenticationClient
interface required for OpenID provider classes. Starting with the source for the Google provider because it has a straight-forward implementation, customize it to make a GenericOpenIdClient
class that works with providers using custom URLs.
要在运行时创建自定义的网址,我们会接受OpenID的用户名作为URI的片段,并与用户名替换的所有实例__名__
中的URL用户提交的。供应商需要以在应用程序启动的URL进行登记,所以在运行时,我们不能只是注册一个供应商的URL时,用户名是众所周知的。
To create the custom URL at runtime, we’ll accept the OpenID user name as a URI fragment, and replace all instances of __username__
in the URL with the user name submitted by the user. Providers need to be registered with URLs during application startup, so we can't just register a provider URL at runtime when the user name is known.
我们将使用OpenID的选择提交表单我们的帐户
与<$ C控制器的 ExternalLogin
行动$ C>提供商表格值设置格式提供商提供者的名称和用户名; {用户名}
。的OpenID选择器内置了从psented给用户一个文本框$ P $与输入到替换 {用户名}
的所有实例的逻辑。在服务器端,我们会从用户名分裂提供者的名称,从这些应用程序启动时注册的名称查找供应商,并设置 GenericOpenIdClient.UserName
属性提交的用户的用户名。
We'll use OpenID Selector to submit a form to our Account
controller’s ExternalLogin
action with the provider
form value set to the provider name and username in the format provider;{username}
. OpenId Selector has logic built in to substitute all instances of {username}
with input from a textbox presented to the user. On the server side, we’ll split the provider name from the username, look up the provider by name from those registered at application startup, and set the GenericOpenIdClient.UserName
property to the username submitted by the user.
在创建认证请求发送到OpenID提供商,我们将检查 GenericOpenIdClient.UserName
属性,并集,我们会重新提供者URL通过发送请求之前的用户名。为了做到这一点,我们需要覆盖 RequestAuthentication()
方法来创建我们的自定义URL的认证请求。 __名__
使用而不是{用户名}
在这里,因为 {
和}
是主机名无效字符,所以创建的URL包括他们变得有问题的时候,我们需要将它们注册为通用供应商的标识符。
When the authentication request is created to send to the OpenID provider, we’ll check the GenericOpenIdClient.UserName
property, and if set, we’ll recreate the provider URL using the username before sending the request. In order to do so, we need to override the RequestAuthentication()
method to create the authentication request with our custom URL. __username__
is used instead of {username}
here because {
and }
aren't valid characters for a hostname, so creating URLs including them becomes problematic when we need to register them as generic provider identifiers.
/GenericOpenIdClient.cs
namespace DotNetOpenAuth.AspNet.Clients
{
using System;
using System.Collections.Generic;
using System.Web;
using System.Xml.Linq;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
using DotNetOpenAuth.OpenId.RelyingParty;
public class GenericOpenIdClient : OpenIdClient
{
#region Constants and Fields
/// <summary>
/// The openid relying party.
/// </summary>
/// <remarks>
/// Pass null as applicationStore to specify dumb mode. Create a protected field to use internally; we can't access the private base class field.
/// </remarks>
protected static readonly OpenIdRelyingParty RelyingParty = new OpenIdRelyingParty(applicationStore: null);
/// <summary>
/// The provider identifier.
/// </summary>
/// <remarks>
/// Create a protected field to use internally; we can't access the private base class field.
/// </remarks>
protected readonly Identifier providerIdentifier;
#endregion
#region Constructors and Destructors
public GenericOpenIdClient(string providerName, Identifier providerIdentifier)
: base(providerName, providerIdentifier)
{
this.providerIdentifier = providerIdentifier; // initialize our internal field as well
}
#endregion
#region Public Properties
public String UserName { get; set; }
#endregion
#region Protected Properties
/// <summary>
/// The provider Identifier with the "__username__" keyword replaced with the value of the UserName property.
/// </summary>
protected Identifier ProviderIdentifier
{
get
{
var customIdentifier = String.IsNullOrWhiteSpace(this.UserName) ?
this.providerIdentifier :
Identifier.Parse(HttpUtility.UrlDecode(this.providerIdentifier).Replace("__username__", this.UserName));
return customIdentifier;
}
}
#endregion
#region Methods
/// <summary>
/// Gets the extra data obtained from the response message when authentication is successful.
/// </summary>
/// <param name="response">
/// The response message.
/// </param>
/// <returns>A dictionary of profile data; or null if no data is available.</returns>
protected override Dictionary<string, string> GetExtraData(IAuthenticationResponse response)
{
FetchResponse fetchResponse = response.GetExtension<FetchResponse>();
if (fetchResponse != null)
{
var extraData = new Dictionary<string, string>();
extraData.AddItemIfNotEmpty("email", fetchResponse.GetAttributeValue(WellKnownAttributes.Contact.Email));
extraData.AddItemIfNotEmpty("country", fetchResponse.GetAttributeValue(WellKnownAttributes.Contact.HomeAddress.Country));
extraData.AddItemIfNotEmpty("firstName", fetchResponse.GetAttributeValue(WellKnownAttributes.Name.First));
extraData.AddItemIfNotEmpty("lastName", fetchResponse.GetAttributeValue(WellKnownAttributes.Name.Last));
return extraData;
}
return null;
}
public override void RequestAuthentication(HttpContextBase context, Uri returnUrl)
{
var realm = new Realm(returnUrl.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped));
IAuthenticationRequest request = RelyingParty.CreateRequest(ProviderIdentifier, realm, returnUrl);
// give subclasses a chance to modify request message, e.g. add extension attributes, etc.
this.OnBeforeSendingAuthenticationRequest(request);
request.RedirectToProvider();
}
/// <summary>
/// Called just before the authentication request is sent to service provider.
/// </summary>
/// <param name="request">
/// The request.
/// </param>
protected override void OnBeforeSendingAuthenticationRequest(IAuthenticationRequest request)
{
// Attribute Exchange extensions
var fetchRequest = new FetchRequest();
fetchRequest.Attributes.AddRequired(WellKnownAttributes.Contact.Email);
fetchRequest.Attributes.AddOptional(WellKnownAttributes.Contact.HomeAddress.Country);
fetchRequest.Attributes.AddRequired(WellKnownAttributes.Name.First);
fetchRequest.Attributes.AddRequired(WellKnownAttributes.Name.Last);
request.AddExtension(fetchRequest);
}
#endregion
}
/// <summary>
/// The dictionary extensions.
/// </summary>
internal static class DictionaryExtensions
{
/// <summary>
/// Adds the value from an XDocument with the specified element name if it's not empty.
/// </summary>
/// <param name="dictionary">
/// The dictionary.
/// </param>
/// <param name="document">
/// The document.
/// </param>
/// <param name="elementName">
/// Name of the element.
/// </param>
public static void AddDataIfNotEmpty(
this Dictionary<string, string> dictionary, XDocument document, string elementName)
{
var element = document.Root.Element(elementName);
if (element != null)
{
dictionary.AddItemIfNotEmpty(elementName, element.Value);
}
}
/// <summary>
/// Adds a key/value pair to the specified dictionary if the value is not null or empty.
/// </summary>
/// <param name="dictionary">
/// The dictionary.
/// </param>
/// <param name="key">
/// The key.
/// </param>
/// <param name="value">
/// The value.
/// </param>
public static void AddItemIfNotEmpty(this IDictionary<string, string> dictionary, string key, string value)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
if (!string.IsNullOrEmpty(value))
{
dictionary[key] = value;
}
}
}
}
要注册建成贡献的微软新DotNetOpenAuth类供应商,取消现有的Microsoft,Facebook,微博,和谷歌供应商,并添加调用注册内置雅虎提供商。在OpenID提供我们即将实现不需要钥匙,但你需要获得来自OAuth的供应商(微软,脸谱,和Twitter)键,如果你想使用它们。在OpenID的选择包中可用的提供的其他部分可以被添加到您的喜好。
To register the providers built into the new DotNetOpenAuth classes contributed by Microsoft, uncomment the existing Microsoft, Facebook, Twitter, and Google providers, and add a call to register the built-in Yahoo provider. The OpenID providers we’re about to implement don’t need keys, but you’ll need to obtain keys from the OAuth providers (Microsoft, Facebook, and Twitter) if you want to use them. The rest of the providers available in the OpenID Selector package can be added to your liking.
/App_Start/AuthConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DotNetOpenAuth.AspNet.Clients;
using DotNetOpenAuth.OpenId.RelyingParty;
using Microsoft.Web.WebPages.OAuth;
using Mvc4ApplicationOpenAuth.Models;
namespace Mvc4ApplicationOpenAuth
{
public static class AuthConfig
{
public static void RegisterAuth()
{
// To let users of this site log in using their accounts from other sites such as Microsoft, Facebook, and Twitter,
// you must update this site. For more information visit http://go.microsoft.com/fwlink/?LinkID=252166
//OAuthWebSecurity.RegisterMicrosoftClient(
// clientId: "",
// clientSecret: "");
//OAuthWebSecurity.RegisterTwitterClient(
// consumerKey: "",
// consumerSecret: "");
//OAuthWebSecurity.RegisterFacebookClient(
// appId: "",
// appSecret: "");
OAuthWebSecurity.RegisterGoogleClient();
OAuthWebSecurity.RegisterYahooClient();
OAuthWebSecurity.RegisterClient(new GenericOpenIdClient("Aol", "https://openid.aol.com/__username__"), "Aol", new Dictionary());
OAuthWebSecurity.RegisterClient(new GenericOpenIdClient("LiveJournal", "https://__username__.livejournal.com/"), "LiveJournal", new Dictionary());
OAuthWebSecurity.RegisterClient(new GenericOpenIdClient("WordPress", "https://__username__.wordpress.com/"), "WordPress", new Dictionary());
OAuthWebSecurity.RegisterClient(new GenericOpenIdClient("Blogger", "https://__username__.blogspot.com/"), "Blogger", new Dictionary());
OAuthWebSecurity.RegisterClient(new GenericOpenIdClient("VeriSign", "https://__username__.pip.verisignlabs.com/"), "VeriSign", new Dictionary());
OAuthWebSecurity.RegisterClient(new GenericOpenIdClient("ClaimID", "https://claimid.com/__username__"), "ClaimID", new Dictionary());
OAuthWebSecurity.RegisterClient(new GenericOpenIdClient("ClickPass", "https://clickpass.com/public/__username__"), "ClickPass", new Dictionary());
OAuthWebSecurity.RegisterClient(new GenericOpenIdClient("Google Profile", "https://www.google.com/profiles/__username__"), "Google Profile", new Dictionary());
OAuthWebSecurity.RegisterClient(new GenericOpenIdClient("MyOpenID", "https://__username__.myopenid.com/"), "MyOpenID", new Dictionary());
}
}
}
最后,我们需要解析提交给帐户提供商形式值
控制器 ExternalLogin
通过OpenID的选择行动检查对于;分隔符,表示用户名是present。如果是这样,那么我们解析出供应商和用户名。
Last, we need parse the provider form value submitted to the Account
controller’s ExternalLogin
action by OpenID Selector to check for the ";" delimiter indicating a username is present. If so, then we parse out the provider name and username.
/Controllers/AccountController.cs
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
if (provider.Contains(';'))
{
string[] providerParts = provider.Split(';');
if (providerParts.Length == 2)
{
AuthenticationClientData clientData;
if (OAuthWebSecurity.TryGetOAuthClientData(providerParts[0], out clientData))
{
var genericClient = clientData.AuthenticationClient as GenericOpenIdClient;
if (genericClient != null)
{
provider = providerParts[0];
genericClient.UserName = providerParts[1];
}
}
}
}
return new ExternalLoginResult(provider, Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
}
用户界面
该UI实现由与开源的OpenID选择更加容易。 下载OpenID的选择并自定义为与 OAuthWebSecurity使用
类。
- 在您的Web应用程序创建一个新的
的OpenID
文件夹:/内容/ OpenID的
- 复制
CSS
,图片
,images.large
和<从code> images.small 文件夹OpenID的选择
下载到/内容/ OpenID的
文件夹,然后包括
在你的项目中的文件。 - 从OpenID的选择下载的
JS
文件夹,复制OpenID的jquery.js和
和OpenID的en.js
来您的Web应用程序的/脚本
文件夹,然后包括文件,您的项目。 - 打开
OpenID的en.js
文件和自定义它,因此提供的网址是
供应商的名称,您将添加在AuthConfig.cs
文件。对于
自定义网址提供商,使用格式提供商; {用户名}
:
- Create a new
openid
folder in your web app at:/Content/openid
- Copy the
css
,images
,images.large
, andimages.small
folders from theopenid-selector
download to the/Content/openid
folder, then include the files in your project. - From the openid-selector download's
js
folder, copyopenid-jquery.js
andopenid-en.js
to your web app's/Scripts
folder, then include the files your project. - Open the
openid-en.js
file and customize it so the provider URLs are the provider names you will add in yourAuthConfig.cs
file. For providers with custom URLs, use the formatProvider;{username}
:
/Scripts/openid-en.js
var providers_large = {
google : {
name : 'Google',
url : 'Google'
},
facebook : {
name : 'Facebook',
url : 'Facebook',
},
twitter: {
name: 'Twitter',
url: 'Twitter'
},
microsoft : {
name : 'Microsoft',
url : 'Microsoft'
},
yahoo : {
name : 'Yahoo',
url : 'Yahoo'
},
aol : {
name : 'Aol',
label : 'Enter your Aol screenname.',
url : 'Aol;{username}'
}
};
var providers_small = {
livejournal: {
name : 'LiveJournal',
label : 'Enter your Livejournal username.',
url: 'LiveJournal;{username}'
},
wordpress : {
name : 'WordPress',
label : 'Enter your WordPress.com username.',
url: 'WordPress;{username}'
},
blogger : {
name : 'Blogger',
label : 'Your Blogger account',
url: 'Blogger;{username}'
},
verisign : {
name : 'VeriSign',
label : 'Your VeriSign username',
url: 'VeriSign;{username}'
},
claimid : {
name : 'ClaimID',
label : 'Your ClaimID username',
url: 'ClaimID;{username}'
},
clickpass : {
name : 'ClickPass',
label : 'Enter your ClickPass username',
url: 'ClickPass;{username}'
},
google_profile : {
name : 'Google Profile',
label : 'Enter your Google Profile username',
url: 'Google Profile;{username}'
},
myopenid: {
name: 'MyOpenID',
label: 'Enter your MyOpenID username.',
url: 'MyOpenID;{username}'
}
};
openid.locale = 'en';
openid.sprite = 'en'; // reused in german& japan localization
openid.demo_text = 'In client demo mode. Normally would have submitted OpenID:';
openid.signin_text = 'Log in';
openid.image_title = 'Log in with {provider}';
openid.no_sprite = true;
openid.img_path = '/Content/openid/images/';
OpenID的选择不来与微软或Twitter图像,因此下载您喜欢的<一个href=\"http://i.microsoft.com/global/ImageStore/PublishingImages/logos/ms-mark/microsoft_logo_90x16.png\">Microsoft和微博(蓝白色)标志,将其转换为GIF格式为100x60像素,然后把它们在 /内容/openid/images.large
文件夹中。阅读在OpenID的选择的说明的README.txt
如果你想,如果单独的图像使用一个单一的精灵形象,而不是文件。设置 openid.no_sprite = FALSE;
在 OpenID的en.js
如果您使用精灵
OpenID Selector doesn’t come with images for Microsoft or Twitter, so download your favorite Microsoft and Twitter (blue on white) logos, convert them to GIFs at 100x60 pixels, then drop them in the /Content/openid/images.large
folder. Read the instructions in the OpenID Selector README.txt
file if you want to use a single sprite image instead if separate images. Set openid.no_sprite = false;
in openid-en.js
if you use the sprite.
注册JS和CSS文件作为一个新包。打开 /App_Start/BundleConfig.cs
,并在 RegisterBundles添加下面的脚本和样式包()
方法。
Register the JS and CSS files as a new bundle. Open /App_Start/BundleConfig.cs
and add the following script and style bundles in the RegisterBundles()
method.
/App_Start/BundleConfig.cs
bundles.Add(new ScriptBundle("~/bundles/openid").Include(
"~/Scripts/openid-jquery.js",
"~/Scripts/openid-en.js"));
bundles.Add(new StyleBundle("~/Content/css/openid").Include("~/Content/openid/css/openid-shadow.css"));
我preFER的OpenID的选择器的影子的风格,所以我选择只使用了 OpenID的shadow.css
CSS文件,并定制了下列类来工作,在MVC4登录模板。
I prefer the OpenID Selector's "shadow" style, so I elected use just the openid-shadow.css
CSS file and customized the following classes to work in the MVC4 Login template.
/Content/css/openid/openid-shadow.css
/*#openid_form {
width: 590px;
}*/
#openid_highlight {
padding: 0px;
background-color: #FFFCC9;
float: left;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
.openid_large_btn {
width: 100px;
height: 60px;
/* fix for IE 6 only: http://en.wikipedia.org/wiki/CSS_filter#Underscore_hack */
_width: 104px;
_height: 64px;
border: 2px solid #DDD;
border-right: 2px solid #ccc;
border-bottom: 2px solid #ccc;
margin: 3px;
padding: 3px;
float: left;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
box-shadow: 2px 2px 4px #ddd;
-moz-box-shadow: 2px 2px 4px #ddd;
-webkit-box-shadow: 2px 2px 4px #ddd;
}
.openid_large_btn:hover {
margin: 4px 3px 3px 6px;
padding: 2px 3px 3px 0px;
border: 2px solid #999;
box-shadow: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
}
要创建一个通用的地方CSS脚本添加到页面的&LT; HEAD&GT;
标签中,添加一个头
在底部的&LT;头方式&gt;
标签
To create a generic place to add CSS scripts to the page's <head>
tag, add a head
section at the bottom of the <head>
tag.
/Views/Shared/_Layout.cshtml
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title - My ASP.NET MVC Application</title>
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
@RenderSection("head", false)
</head>
然后,在 /Views/Account/Login.cshtml
文件中,加入自定义登录
视图我们在页面的底部注册的OpenID包previously到相应的部分。
Then, in the /Views/Account/Login.cshtml
file, customize the Login
view by adding the OpenID bundles we registered previously to the appropriate sections at the bottom of the page.
/Views/Account/Login.cshtml
<section class="social" id="socialLoginForm">
@Html.Action("ExternalLoginsList", new { ReturnUrl = ViewBag.ReturnUrl })
</section>
@section Head {
@Styles.Render("~/Content/css/openid")
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
@Scripts.Render("~/bundles/openid")
<script type="text/javascript">
$(function () {
openid.init('provider');
});
</script>
}
用户界面的最后一个元素涉及与OpenID的选择形式替换默认 ExternalLogin
的形式。
/Views/Account/_ExternalLoginsListPartial.cshtml
using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { id = "openid_form" }))
{
@Html.AntiForgeryToken()
<input type="hidden" name="action" value="verify" />
<h2>Use another service to log in.</h2>
<br />
<fieldset id="socialLoginList">
<legend></legend>
<div id="openid_choice">
<div id="openid_btns"></div>
</div>
<div id="openid_input_area">
<input id="provider" name="provider" type="text" value="" />
<input id="openid_submit" type="submit" value="Log in"/>
</div>
<noscript>
<p>OpenID is service that allows you to log-on to many different websites using a single indentity. Find out <a href="http://openid.net/what/">more about OpenID</a> and <a href="http://openid.net/get/">how to get an OpenID enabled account</a>.</p>
</noscript>
</fieldset>
}
这篇关于如何使用ASP.NET中MVC4唯一标识符的URL OpenID提供的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!