从代码配置 Windows Identity Foundation

发布于 2024-10-16 06:44:38 字数 2349 浏览 5 评论 0 原文

我正在尝试“无配置 WIF”,我想接受由 Windows Azure 的 AppFabric STS 生成的 SAML2 令牌。

我正在做的是解析检查当前请求的令牌信息,如下所示:

        if (Request.Form.Get(WSFederationConstants.Parameters.Result) != null)
        {
            SignInResponseMessage message = 
                WSFederationMessage.CreateFromFormPost(System.Web.HttpContext.Current.Request) as SignInResponseMessage;

            var securityTokenHandlers = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();                    

            XmlTextReader xmlReader = new XmlTextReader(
                new StringReader(message.Result));

            SecurityToken token = securityTokenHandlers.ReadToken(xmlReader);

            if (token != null)
            {
                ClaimsIdentityCollection claims = securityTokenHandlers.ValidateToken(token);
                IPrincipal principal = new ClaimsPrincipal(claims);
            }
        }

上面的代码使用 SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();用于验证和处理 SAML 令牌的集合。但是:这不起作用,因为显然应用程序尚未正确配置。如何在我的 securityTokenHandlers 集合上以编程方式从 XML 指定以下配置?

  <microsoft.identityModel>
<service>
  <audienceUris>
    <add value="http://www.someapp.net/" />
  </audienceUris>
  <federatedAuthentication>
    <wsFederation passiveRedirectEnabled="true" issuer="https://rd-test.accesscontrol.appfabriclabs.com/v2/wsfederation" realm="http://www.thisapp.net" requireHttps="false" />
    <cookieHandler requireSsl="false" />
  </federatedAuthentication>
  <applicationService>
    <claimTypeRequired>
      <claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true" />
      <claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true" />
    </claimTypeRequired>
  </applicationService>
  <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
    <trustedIssuers>
      <add thumbprint="XYZ123" name="https://somenamespace.accesscontrol.appfabriclabs.com/" />
    </trustedIssuers>
  </issuerNameRegistry>
</service>

I'm experimenting with "configuration-less WIF", where I want to accept a SAML2 token that is generated by Windows Azure's AppFabric STS.

What I'm doing is parsing checking the current request for token information, like so:

        if (Request.Form.Get(WSFederationConstants.Parameters.Result) != null)
        {
            SignInResponseMessage message = 
                WSFederationMessage.CreateFromFormPost(System.Web.HttpContext.Current.Request) as SignInResponseMessage;

            var securityTokenHandlers = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();                    

            XmlTextReader xmlReader = new XmlTextReader(
                new StringReader(message.Result));

            SecurityToken token = securityTokenHandlers.ReadToken(xmlReader);

            if (token != null)
            {
                ClaimsIdentityCollection claims = securityTokenHandlers.ValidateToken(token);
                IPrincipal principal = new ClaimsPrincipal(claims);
            }
        }

The code above uses the SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection(); colection to verify and handle the SAML token. However: this does not work because obviously the application has not bee nconfigured correctly. How would I specify the follwing configuration from XML programmaticaly on my securityTokenHandlers collection?

  <microsoft.identityModel>
<service>
  <audienceUris>
    <add value="http://www.someapp.net/" />
  </audienceUris>
  <federatedAuthentication>
    <wsFederation passiveRedirectEnabled="true" issuer="https://rd-test.accesscontrol.appfabriclabs.com/v2/wsfederation" realm="http://www.thisapp.net" requireHttps="false" />
    <cookieHandler requireSsl="false" />
  </federatedAuthentication>
  <applicationService>
    <claimTypeRequired>
      <claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true" />
      <claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true" />
    </claimTypeRequired>
  </applicationService>
  <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
    <trustedIssuers>
      <add thumbprint="XYZ123" name="https://somenamespace.accesscontrol.appfabriclabs.com/" />
    </trustedIssuers>
  </issuerNameRegistry>
</service>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

隱形的亼 2024-10-23 06:44:38

我也在努力解决同样的问题,并在 WIF 3.5/4.0 中找到了一个可行的解决方案。由于 maartenba 的链接似乎已失效,我想在这里发布我的解决方案。

我们的要求是:

  • 完全用代码进行配置(因为我们随应用程序提供了默认的 web.config)
  • 允许的最大 .Net 版本 4.0(因此我使用 WIF 3.5/4.0)

我用来得出解决方案的内容:

  • 有关动态 WIF 的信息配置由吴彦祖提供
    这里
  • 这个
    方法

    在运行时注册 HTTP 模块,由 David Ebbo 解释。我
    还尝试了更优雅的方法 由 Rick 解释
    斯特拉尔

    但不幸的是,这对我来说并没有成功。

编辑2016/09/02:而不是添加单独的“预应用程序启动”
code”类,如 David Ebbo 的示例,WIF 相关的 HTTP 模块
也可以在静态构造函数中注册
“HttpApplication”类。我已经对此代码进行了一些调整
更清洁的解决方案。

我的解决方案在 web.config 中不需要任何内容​​。大部分代码位于 global.asax.cs 中。此示例中的配置是硬编码的:

using System;
using System.IdentityModel.Selectors;
using System.Security.Cryptography.X509Certificates;
using System.Web;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Web;

namespace TestADFS
{
  public class SessionAuthenticationModule : Microsoft.IdentityModel.Web.SessionAuthenticationModule
  {
    protected override void InitializePropertiesFromConfiguration(string serviceName)
    {
    }
  }
  public class WSFederationAuthenticationModule : Microsoft.IdentityModel.Web.WSFederationAuthenticationModule
  {
    protected override void InitializePropertiesFromConfiguration(string serviceName)
    {
      ServiceConfiguration = FederatedAuthentication.ServiceConfiguration;
      PassiveRedirectEnabled = true;
      RequireHttps = true;
      Issuer = "https://nl-joinadfstest.joinadfstest.local/adfs/ls/";
      Realm = "https://67px95j.decos.com/testadfs";
    }
  }

  public class Global : HttpApplication
  {
    static Global()
    {
      Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(SessionAuthenticationModule));
      Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(WSFederationAuthenticationModule));
    }

    protected void Application_Start(object sender, EventArgs e)
    {
      FederatedAuthentication.ServiceConfigurationCreated += FederatedAuthentication_ServiceConfigurationCreated;
    }

    internal void FederatedAuthentication_ServiceConfigurationCreated(object sender, Microsoft.IdentityModel.Web.Configuration.ServiceConfigurationCreatedEventArgs e)
    {
      X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
      store.Open(OpenFlags.ReadOnly);
      X509Certificate2Collection coll = store.Certificates.Find(X509FindType.FindByThumbprint, "245537E9BB2C086D3C880982FA86267FBD66B9A3", false);
      if (coll.Count > 0)
        e.ServiceConfiguration.ServiceCertificate = coll[0];
      store.Close();
      AudienceRestriction ar = new AudienceRestriction(AudienceUriMode.Always);
      ar.AllowedAudienceUris.Add(new Uri("https://67px95j.decos.com/testadfs"));
      e.ServiceConfiguration.AudienceRestriction = ar;
      ConfigurationBasedIssuerNameRegistry inr = new ConfigurationBasedIssuerNameRegistry();
      inr.AddTrustedIssuer("6C9B96D90257B65B6F181C2478D869473DC359EA", "http://NL-JOINADFSTEST.joinadfstest.local/adfs/services/trust");
      e.ServiceConfiguration.IssuerNameRegistry = inr;
      e.ServiceConfiguration.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;
    }

    protected void Application_AuthenticateRequest(object sender, EventArgs e)
    {
      FederatedAuthentication.WSFederationAuthenticationModule.ServiceConfiguration = FederatedAuthentication.ServiceConfiguration;
    }
  }
}

用法

我的应用程序是 asp.net WebForms,在经典管道模式下运行,支持表单身份验证以及 ADFS 登录。因此,身份验证是在所有 .aspx 页面共享的公共基类中处理的:

    protected override void OnInit(EventArgs e)
    {
      if (NeedsAuthentication && !User.Identity.IsAuthenticated)
      {
        SignInRequestMessage sirm = new SignInRequestMessage(
          new Uri("https://nl-joinadfstest.joinadfstest.local/adfs/ls/"),
          ApplicationRootUrl)
        {
          Context = ApplicationRootUrl,
          HomeRealm = ApplicationRootUrl
        };
        Response.Redirect(sirm.WriteQueryString());
      }
      base.OnInit(e);
    }

在此代码中,ApplicationRootUrl 是以“/”结尾的应用程序路径(“/”在经典管道中很重要)模式)。

由于混合模式下注销的稳定实现并不那么容易,因此我也想展示其代码。从技术上讲它是有效的,但我在注销 ADFS 帐户后立即再次登录 IE 仍然存在问题:(

      if (User.Identity.IsAuthenticated)
      {
        if (User.Identity.AuthenticationType == "Forms")
        {
          FormsAuthentication.SignOut();
          Session.Clear();
          Session.Abandon();
          ResetCookie(FormsAuthentication.FormsCookieName);
          ResetCookie("ASP.NET_SessionId");
          Response.Redirect(ApplicationRootUrl + "Default.aspx");
          HttpContext.Current.ApplicationInstance.CompleteRequest();
        }
        else
        {
          FederatedAuthentication.SessionAuthenticationModule.SignOut();
          FederatedAuthentication.SessionAuthenticationModule.DeleteSessionTokenCookie();
          Uri uri = new Uri(ApplicationRootUrl + "Default.aspx");
          WSFederationAuthenticationModule.FederatedSignOut(
            new Uri("https://nl-joinadfstest.joinadfstest.local/adfs/ls/"),
            uri); // 1st url is single logout service binding from adfs metadata
        }
      }

ResetCookie 是一个帮助函数,可清除响应 cookie 并设置其过期时间)

I was struggling with the same and found a working solution in WIF 3.5/4.0. Since maartenba's link seems to be dead, I wanted to post my solution here.

Our requirements were:

  • Configuration fully in code (as we ship a default web.config with the app)
  • Maximum allowed .Net version 4.0 (hence I am using WIF 3.5/4.0)

What I used to arrive at the solution:

  • Information about dynamic WIF configuration provided by Daniel Wu
    here.
  • This
    method

    to register HTTP modules at runtime, explained by David Ebbo. I
    also tried the more elegant method explained by Rick
    Strahl
    ,
    but that unfortunately did not do the trick for me.

Edit 2016/09/02: instead of adding a separate "pre application start
code" class as in David Ebbo's example, the WIF-related HTTP modules
can also be registered in the static constructor of the
`HttpApplication' class. I have adapted the code to this somewhat
cleaner solution.

My solution needs nothing in web.config. The bulk of the code is in global.asax.cs. Configuration is hard-coded in this sample:

using System;
using System.IdentityModel.Selectors;
using System.Security.Cryptography.X509Certificates;
using System.Web;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Web;

namespace TestADFS
{
  public class SessionAuthenticationModule : Microsoft.IdentityModel.Web.SessionAuthenticationModule
  {
    protected override void InitializePropertiesFromConfiguration(string serviceName)
    {
    }
  }
  public class WSFederationAuthenticationModule : Microsoft.IdentityModel.Web.WSFederationAuthenticationModule
  {
    protected override void InitializePropertiesFromConfiguration(string serviceName)
    {
      ServiceConfiguration = FederatedAuthentication.ServiceConfiguration;
      PassiveRedirectEnabled = true;
      RequireHttps = true;
      Issuer = "https://nl-joinadfstest.joinadfstest.local/adfs/ls/";
      Realm = "https://67px95j.decos.com/testadfs";
    }
  }

  public class Global : HttpApplication
  {
    static Global()
    {
      Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(SessionAuthenticationModule));
      Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(WSFederationAuthenticationModule));
    }

    protected void Application_Start(object sender, EventArgs e)
    {
      FederatedAuthentication.ServiceConfigurationCreated += FederatedAuthentication_ServiceConfigurationCreated;
    }

    internal void FederatedAuthentication_ServiceConfigurationCreated(object sender, Microsoft.IdentityModel.Web.Configuration.ServiceConfigurationCreatedEventArgs e)
    {
      X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
      store.Open(OpenFlags.ReadOnly);
      X509Certificate2Collection coll = store.Certificates.Find(X509FindType.FindByThumbprint, "245537E9BB2C086D3C880982FA86267FBD66B9A3", false);
      if (coll.Count > 0)
        e.ServiceConfiguration.ServiceCertificate = coll[0];
      store.Close();
      AudienceRestriction ar = new AudienceRestriction(AudienceUriMode.Always);
      ar.AllowedAudienceUris.Add(new Uri("https://67px95j.decos.com/testadfs"));
      e.ServiceConfiguration.AudienceRestriction = ar;
      ConfigurationBasedIssuerNameRegistry inr = new ConfigurationBasedIssuerNameRegistry();
      inr.AddTrustedIssuer("6C9B96D90257B65B6F181C2478D869473DC359EA", "http://NL-JOINADFSTEST.joinadfstest.local/adfs/services/trust");
      e.ServiceConfiguration.IssuerNameRegistry = inr;
      e.ServiceConfiguration.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;
    }

    protected void Application_AuthenticateRequest(object sender, EventArgs e)
    {
      FederatedAuthentication.WSFederationAuthenticationModule.ServiceConfiguration = FederatedAuthentication.ServiceConfiguration;
    }
  }
}

Usage

My app is asp.net WebForms, running in classic pipeline mode and supports forms authentication as well as ADFS login. Because of that, authentication is handled in a common base class shared by all .aspx pages:

    protected override void OnInit(EventArgs e)
    {
      if (NeedsAuthentication && !User.Identity.IsAuthenticated)
      {
        SignInRequestMessage sirm = new SignInRequestMessage(
          new Uri("https://nl-joinadfstest.joinadfstest.local/adfs/ls/"),
          ApplicationRootUrl)
        {
          Context = ApplicationRootUrl,
          HomeRealm = ApplicationRootUrl
        };
        Response.Redirect(sirm.WriteQueryString());
      }
      base.OnInit(e);
    }

In this code, ApplicationRootUrl is the application path ending in "/" (the "/" is important in Classic pipeline mode).

As a stable implementation for logout in mixed mode was not so easy, I want to show the code for that as well. Technically it works, but I still have an issue with IE immediately logging in again after logging out an ADFS account:

      if (User.Identity.IsAuthenticated)
      {
        if (User.Identity.AuthenticationType == "Forms")
        {
          FormsAuthentication.SignOut();
          Session.Clear();
          Session.Abandon();
          ResetCookie(FormsAuthentication.FormsCookieName);
          ResetCookie("ASP.NET_SessionId");
          Response.Redirect(ApplicationRootUrl + "Default.aspx");
          HttpContext.Current.ApplicationInstance.CompleteRequest();
        }
        else
        {
          FederatedAuthentication.SessionAuthenticationModule.SignOut();
          FederatedAuthentication.SessionAuthenticationModule.DeleteSessionTokenCookie();
          Uri uri = new Uri(ApplicationRootUrl + "Default.aspx");
          WSFederationAuthenticationModule.FederatedSignOut(
            new Uri("https://nl-joinadfstest.joinadfstest.local/adfs/ls/"),
            uri); // 1st url is single logout service binding from adfs metadata
        }
      }

(ResetCookie is a helper function that clears a response cookie and sets its expiration in the past)

新人笑 2024-10-23 06:44:38

只是一个想法,不知道这是否有效:有没有办法获取实际的 XML(在您的情况下为空)并在运行时通过 Microsoft.IdentityModel.Configuration

或者,您可以在发送登录请求时修改 XML 中的某些内容,位于 通过修改 SignInRequestMessage 实现 RedirectingToIdentityProvider 事件

Just a thought, no idea whether this works: Isn't there a way to get at the actual XML (which is empty in your case) and modify it at runtime through the classes in Microsoft.IdentityModel.Configuration?

Alternatively, some of the things in the XML you can modify at the time the sign-in request is sent out, in the RedirectingToIdentityProvider event by modifying the SignInRequestMessage

咿呀咿呀哟 2024-10-23 06:44:38

仅供参考:找到一个解决方案并在此处描述(和链接)的模块中实现它: http://blog.maartenballiauw.be/post/2011/02/14/Authenticate-Orchard-users-with-AppFabric-Access-Control-Service.aspx

FYI: found a solution and implemented it in a module described (and linked) here: http://blog.maartenballiauw.be/post/2011/02/14/Authenticate-Orchard-users-with-AppFabric-Access-Control-Service.aspx

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文