gpt4 book ai didi

asp.net-mvc - 如何在 ASP.NET MVC4 中使用具有唯一标识符 URL 的 OpenID 提供程序

转载 作者:行者123 更新时间:2023-12-04 01:49:38 25 4
gpt4 key购买 nike

新款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 中使用带有用户名的唯一标识符时?

最佳答案

我开发了以下适用于我的解决方案,我正在分享,以防它对其他人有所帮助,但我真的很想看看是否有更直接的方法或我缺少的“最佳实践”。

基本上,你需要实现一个 OpenIdClientProviderIdentifier 初始化其 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 ExternalLoginprovider 一起行动表单值设置为提供者名称和用户名,格式为 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;
}
}
}
}

要注册 Microsoft 提供的新 DotNetOpenAuth 类中内置的提供程序,请取消对现有 Microsoft、Facebook、Twitter 和 Google 提供程序的注释,并添加一个调用以注册内置的 Yahoo 提供程序。我们即将实现的 OpenID 提供程序不需要 key ,但如果您想使用它们,则需要从 OAuth 提供程序(Microsoft、Facebook 和 Twitter)获取 key 。可以根据自己的喜好添加 OpenID Selector 包中可用的其他提供程序。

/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());
}
}
}

最后,我们需要解析提交给 Account 的提供者表单值。 Controller ExternalLogin OpenID 选择器的操作以检查“;”指示用户名存在的分隔符。如果是这样,那么我们解析出提供者名称和用户名。

/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 }));
}

用户界面

使用开源 OpenID Selector,UI 实现变得更加容易。 Download OpenID Selector并定制它以与 OAuthWebSecurity 一起使用类。
  • 新建 openid您的 Web 应用程序中的文件夹:/Content/openid
  • 复制 css , images , images.large , 和 images.small文件夹从openid-selector下载到/Content/openid文件夹,然后包括
    项目中的文件。
  • 来自 openid-selector 下载的 js文件夹,复制 openid-jquery.jsopenid-en.js到您的网络应用程序的 /Scripts文件夹,然后包含您的项目文件。
  • 打开 openid-en.js文件并对其进行自定义,以便提供程序 URL 是
    您将在 AuthConfig.cs 中添加的提供商名称文件。为了
    具有自定义 URL 的提供程序,使用格式 Provider;{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 选择器不附带适用于 Microsoft 或 Twitter 的图像,因此请下载您最喜欢的 MicrosoftTwitter (蓝底白字)徽标,将它们转换为 100x60 像素的 GIF,然后将它们放入 /Content/openid/images.large文件夹。阅读 OpenID 选择器中的说明 README.txt如果要使用单个 Sprite 图像而不是单独的图像。套装 openid.no_sprite = false;openid-en.js如果你使用 Sprite 。

    将 JS 和 CSS 文件注册为新包。开通 /App_Start/BundleConfig.cs并在 RegisterBundles() 中添加以下脚本和样式包方法。

    /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"));

    我更喜欢 OpenID 选择器的“影子”风格,所以我选择只使用 openid-shadow.css CSS 文件并自定义以下类以在 MVC4 登录模板中工作。

    /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 脚本添加到页面的 <head>标签,添加 head <head>底部的部分标签。

    /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文件,自定义 Login通过将我们之前注册的 OpenID 包添加到页面底部的相应部分来查看。

    /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>
    }

    UI 的最后一个元素涉及替换默认值 ExternalLogin带有 OpenID 选择器表单的表单。

    /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-mvc - 如何在 ASP.NET MVC4 中使用具有唯一标识符 URL 的 OpenID 提供程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12878441/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com