- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
新款SimpleMembershipProvider
在 ASP.NET MVC4 中实现的 ASP.NET MVC4 允许对两个流行的 OpenID 提供者(谷歌和雅虎)和三个 OAuth 提供者(微软、Facebook、Twitter)提供简单的内置支持。
在 DotNetOpenAuth.AspNet.Clients
中实现的提供程序用于 SimpleMembershipProvider
所有的身份服务都使用静态 URL --- 也就是说,所有用户都使用相同的众所周知的 URL 来访问提供者。用户的 OpenID 标识符与用于访问身份服务的 URL 是分开的。
例如,Google 的 OpenID 服务 URL 是 https://www.google.com/accounts/o8/id
对于所有用户。
这适用于 SimpleMembershipProvider
在 MVC4 中,身份提供者的 URL 需要在您的 MVC 应用程序启动时知道、保持不变并注册。
问题是,其他 OpenID 提供商通常使用用户唯一的 OpenID 标识符作为 URL 来访问身份服务。
例如,AOL 和 WordPress 使用 https://openid.aol.com/{username}
和 https://{username}.wordpress.com
, 分别。
如果更换 SimpleMembershipProvider
使用您自己的 ExtendedMembershipProvider
实现,然后您可以推出自己的提供程序实现,但是它不适用于 MVC4 Account
开箱即用的 Controller 。
如何使用 SimpleMembershipProvider
实现新的 OpenID 依赖方,当提供者在 URL 中使用带有用户名的唯一标识符时?
最佳答案
我开发了以下适用于我的解决方案,我正在分享,以防它对其他人有所帮助,但我真的很想看看是否有更直接的方法或我缺少的“最佳实践”。
基本上,你需要实现一个 OpenIdClient
用 ProviderIdentifier
初始化其 URL 包含关键字 __username__
.
在运行时,提供者名称和用户名被传递给 Account
Controller ,其中提供者客户端是按名称选择的,用户名替换为 __username__
将身份验证请求发送给提供者之前的关键字。
OpenID 客户端
Microsoft 贡献的 DotNetOpenAuth OpenID 提供程序类继承了基类 DotNetOpenAuth.AspNet.Clients.OpenIdClient
,它实现了 IAuthenticationClient
OpenID 提供程序类所需的接口(interface)。从 source for the Google provider 开始因为它有一个直接的实现,自定义它以制作 GenericOpenIdClient
使用自定义 URL 与提供程序一起使用的类。
要在运行时创建自定义 URL,我们将接受 OpenID 用户名作为 URI 片段,并替换 __username__
的所有实例。在用户提交的用户名的 URL 中。 Providers 需要在应用程序启动时用 URL 注册,所以我们不能在知道用户名的情况下在运行时注册一个提供程序 URL。
我们将使用 OpenID Selector 向我们的 Account
提交表单。 Controller ExternalLogin
与 provider
一起行动表单值设置为提供者名称和用户名,格式为 provider;{username}
. OpenId Selector 具有内置逻辑来替换 {username}
的所有实例来自文本框的输入呈现给用户。在服务器端,我们将从用户名中分离提供者名称,从应用程序启动时注册的名称中查找提供者,并设置 GenericOpenIdClient.UserName
属性为用户提交的用户名。
创建身份验证请求以发送到 OpenID 提供程序时,我们将检查 GenericOpenIdClient.UserName
属性,如果设置,我们将在发送请求之前使用用户名重新创建提供者 URL。为此,我们需要覆盖 RequestAuthentication()
使用我们的自定义 URL 创建身份验证请求的方法。 __username__
用于代替 {username}
这里是因为 {
和 }
不是主机名的有效字符,因此当我们需要将它们注册为通用提供程序标识符时,创建包含它们的 URL 会出现问题。
/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;
}
}
}
}
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());
}
}
}
Account
的提供者表单值。 Controller
ExternalLogin
OpenID 选择器的操作以检查“;”指示用户名存在的分隔符。如果是这样,那么我们解析出提供者名称和用户名。
[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 }));
}
OAuthWebSecurity
一起使用类。
openid
您的 Web 应用程序中的文件夹:/Content/openid
css
, images
, images.large
, 和 images.small
文件夹从openid-selector
下载到/Content/openid
文件夹,然后包括js
文件夹,复制 openid-jquery.js
和 openid-en.js
到您的网络应用程序的 /Scripts
文件夹,然后包含您的项目文件。 openid-en.js
文件并对其进行自定义,以便提供程序 URL 是AuthConfig.cs
中添加的提供商名称文件。为了Provider;{username}
: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/';
/Content/openid/images.large
文件夹。阅读 OpenID 选择器中的说明
README.txt
如果要使用单个 Sprite 图像而不是单独的图像。套装
openid.no_sprite = false;
在
openid-en.js
如果你使用 Sprite 。
/App_Start/BundleConfig.cs
并在
RegisterBundles()
中添加以下脚本和样式包方法。
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"));
openid-shadow.css
CSS 文件并自定义以下类以在 MVC4 登录模板中工作。
/*#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;
}
<head>
标签,添加
head
<head>
底部的部分标签。
<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
文件,自定义
Login
通过将我们之前注册的 OpenID 包添加到页面底部的相应部分来查看。
<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>
}
ExternalLogin
带有 OpenID 选择器表单的表单。
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-mvc - 如何在 ASP.NET MVC4 中使用具有唯一标识符 URL 的 OpenID 提供程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12878441/
OpenID 是否改善了用户体验? 编辑 不是要贬低其他评论,但我在下面得到了一个非常好的回复,它以一种合理的底线方式概述了 OpenID 的 3 个优点。我还在其他评论中听到了一些耳语,你可以通过
我不想疏远我的用户,但是通过多种方式登录网站违背了实现 OpenID 的目的。这不是一个全新的网站,大约有 3000 名用户是顽固的(我们有一个很棒的社区),但并非所有人都是顽固的,我不想因为登录过程
我在 StackOverflow 上使用 OpenID 来验证我的用户身份,这与这里的使用方式非常相似。我真正需要做的是让 OpenID 在我网站的所有子域中工作。 该站点的行为与 Kijiji 大致
OpenID 有没有其他选择,我的意思是一个提供我们详细信息的站点,我们可以使用该 ID 登录到一个站点? 更新 假设认为我正在开发一个具有 openID 集成的网站,我将只有一个用户名,没有那么多细
有人可以帮助我了解 OpenID 的工作原理吗?我对以下答案感兴趣: 如果使用 OpenId,您还需要存储用户 ID 和密码吗? 当用户登录时,我的应用程序如何创建新 session ? 当用户退出应
最重要的问题是我的电子邮件地址是否被传输到消费服务。 例如,如果我使用 Google 在这里登录 SO,那么 SO 知道我的 gmail 地址吗? 他知道我在 gmail 设置中输入的用于外发邮件的名
OpenID 连接的当前状态是什么?我想将它用于新的 SSO 系统。是否有任何库可用于实现? 最佳答案 最终 OpenID Connect 规范于 2014 年 2 月 26 日根据 http://o
作为单点登录实现,我认为 OpenID 很棒。即便如此,它是否一定是电子商务的好选择?我知道它可以使用,但应该使用吗?您是否冒险将所有访问详细信息放在一个篮子中? 那里的普遍意见是什么? 最佳答案 当
我不是问具体的实现,也不是问跨站单点登录机制的全局世界观,我只是想知道社区对 OpenID 底层可用性的看法。您是否认为使用由(非技术观察者)随机提供的各种提供者发布的 URL 来代替实际的用户名是人
已关闭。这个问题是 off-topic 。目前不接受答案。 想要改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 已关闭11 年前。 Improve th
我是openID的新手。我花了很多时间思考最好的做法是为用户提供选择,并使用各种启用了OpenID的帐户登录。 (我必须进一步澄清,我的系统不是一个只需要简单的“一次性身份验证”即可进行博客发布的系统
我希望将 OpenID 实现为一组合作伙伴网站的提供商。问题是这个网站是为 child (13 岁以下)准备的,所以我有一些业务限制需要处理 - 主要是帐户不能用于任何非合作伙伴网站(我们与每个获得批
WordPress 的 OpenID 插件似乎不接受任何 Google OpenID 提供商链接: http://google.com/profiles/username 或 https://goog
我在看 DotNetOpenAuth samples并且有两个 OpenID 提供程序示例;和 OpenID 提供程序和一个 OpenID Web Ring SSO 提供程序。 有谁知道两者之间的区别
存在一个行为不当的 OpenID Connect“兼容”iDP(它现在应该保持无名)——它在使用范围 openid 和任何包含 id_token 的 response_type 时抛出错误。这肯定是一
有没有人知道或有任何我可以用来构建使用 OpenID 的站点的文档?例如,当用户访问我的网站时,我接受一个 OpenID,然后我会将他们重定向到 OpenID 提供商,然后当他们通过身份验证时,他们将
现在我使用 3rd 方网站作为我的 openid 提供者(myvidoop 和 myopenid)。我正在考虑让我的网站充当我的提供者。我认为肯定会有一些脚本可以安装并轻松实现。我已经尝试过 janr
我有用户帐户的本地表 用户 ID(nvarchar)、密码、电子邮件、姓氏角色等。 现在每个子表中都使用了 UserID。我想在我的站点中启用 OPENID 注册,稍后将集成本地用户注册功能。我应该将
我遇到了 google openid 标识符的问题。 我在简单的 asp.net mvc 项目中使用 dotnetopenauth 库(dotnetopenid 的继承者)。 在本地主机上的测试期间,
我对 OpenId 很陌生,并且在身份验证完成后理解如何使用 OpenId 时遇到了一些问题。 我正在创建一个新站点,并且在使 openId 身份验证正常工作方面没有问题。但是我不确定一旦用户登录,我
我是一名优秀的程序员,十分优秀!