Пользовательский WIF запрос валидатора бесконечный цикл

Исправляя проблему, описанную здесь, я создал еще одну, которую я могу найти где-нибудь еще в Интернете:

SignInResponseMessage message = WSFederationMessage.CreateFromFormPost(context.Request) as SignInResponseMessage;

Приведенный выше код продолжает вызывать IsValidRequestString(), что вызывает бесконечный цикл. Вызов каждый раз один и тот же, и стек выглядит так:

>   TestIdentityBroker.dll!TestIdentityBroker.Service.WsFederationRequestValidator.IsValidRequestString(System.Web.HttpContext context, string value, System.Web.Util.RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex) Line 19    C#
[External Code] 
TestIdentityBroker.dll!TestIdentityBroker.Service.WsFederationRequestValidator.IsValidRequestString(System.Web.HttpContext context, string value, System.Web.Util.RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex) Line 19 + 0x21 bytes   C#
[External Code] 
TestIdentityBroker.dll!TestIdentityBroker.Service.WsFederationRequestValidator.IsValidRequestString(System.Web.HttpContext context, string value, System.Web.Util.RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex) Line 19 + 0x21 bytes   C#
[External Code] 
TestIdentityBroker.dll!TestIdentityBroker.Service.WsFederationRequestValidator.IsValidRequestString(System.Web.HttpContext context, string value, System.Web.Util.RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex) Line 19 + 0x21 bytes   C#
[External Code] 

Это происходит в пользовательской службе маркеров безопасности проверяющей стороны, которая объединяет идентификационные данные моей проверяющей стороны с ip, созданным инструментом FedUtil. Кто-нибудь знает почему WSFederationMessage.CreateFromFormPost() будет называть запрос валидатором? Возвращенный неправильно кажется нормальным.

РЕДАКТИРОВАТЬ: Это происходит только тогда, когда я уже был аутентифицирован один раз. Если я очищу кеш браузера, этого не произойдет.

<?xml version="1.0"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=152368
  -->
<configuration>
  <configSections>
    <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  </configSections>
  <connectionStrings>
    <add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <appSettings>
    <add key="webpages:Version" value="1.0.0.0" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="FederationMetadataLocation" value="X:\WebTest\TestIdentityBroker\TestIdentityBroker_STS\FederationMetadata\2007-06\FederationMetadata.xml" />
    <add key="SigningCertificateName" value="CN=Dev4"/>
  </appSettings>
  <location path="FederationMetadata">
    <system.web>
      <authorization>
        <allow users="*" />
      </authorization>
    </system.web>
  </location>
  <system.web>
    <!--<authorization>
      <deny users="?" />
    </authorization>-->
    <compilation debug="true" targetFramework="4.0">
      <assemblies>
        <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      </assemblies>
    </compilation>
    <!--<authentication mode="Forms">
      <forms loginUrl="~/Federation/Authenticate" timeout="2880" />
    </authentication>-->
    <authentication mode="None" />
    <membership>
      <providers>
        <clear />
        <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
      </providers>
    </membership>
    <profile>
      <providers>
        <clear />
        <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/" />
      </providers>
    </profile>
    <roleManager enabled="false">
      <providers>
        <clear />
        <add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="ApplicationServices" applicationName="/" />
        <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
      </providers>
    </roleManager>
    <pages>
      <namespaces>
        <add namespace="System.Web.Helpers" />
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages" />
      </namespaces>
    </pages>
    <httpRuntime requestValidationType="TestIdentityBroker.Service.WsFederationRequestValidator" />
    <httpModules>
      <add name="WSFederationAuthenticationModule" type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      <add name="SessionAuthenticationModule" type="Microsoft.IdentityModel.Web.SessionAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </httpModules>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules runAllManagedModulesForAllRequests="true">
      <add name="WSFederationAuthenticationModule" type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" />
      <add name="SessionAuthenticationModule" type="Microsoft.IdentityModel.Web.SessionAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" />
    </modules>
  </system.webServer>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <microsoft.identityModel>
    <service>
      <audienceUris>
        <add value="https://rp_sts.local/" />
        <add value="https://rp_sts.local/Federation/LogOn" />
      </audienceUris>
      <federatedAuthentication>
        <wsFederation passiveRedirectEnabled="false" issuer="https://ip.local/" realm="https://rp_sts.local/" requireHttps="false" />
        <cookieHandler requireSsl="true" />
      </federatedAuthentication>
      <applicationService>
        <claimTypeRequired>
          <!--Following are the claims offered by STS 'http://ip.local/'. Add or uncomment claims that you require by your application and then update the federation metadata of this application.-->
          <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="xx" name="https://ip.local/" />
        </trustedIssuers>
      </issuerNameRegistry>
    </service>
  </microsoft.identityModel>
</configuration>

3 ответа

Проблема заключается в том, что при аутентификации пользователя доступ к System.HttpContext.Current.Request на всех запускает RequestValidator для выполнения.

Таким образом, передача ссылки System.HttpContext.Current.Request в WSFederationMessage.CreateFromFormPost внутри RequestValidator инициирует бесконечный цикл. Я не изучал, почему возникает эта проблема.

Хотя вы можете решить не обрабатывать запросы пользователей, уже прошедших проверку подлинности на вашем STS (как вы это сделали), это не сработает, если ваш код является промежуточным STS, который зависит от другого поставщика. Например, если запрашивающий RP передает другой WHR, вы все равно захотите обработать его до родительского STS в случае, если разные домашние области выдают разные заявки.

В моем случае я был вынужден изменить свой валидатор запроса, просто возвращая true, если присутствует параметр wresult. При этом ответственность за проверку поступающего сообщения делегируется коду, который обрабатывает запросы на вход:

public class WIFRequestValidator : RequestValidator
{
    protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex)
    {
        validationFailureIndex = 0;

        if (requestValidationSource == RequestValidationSource.Form && collectionKey.Equals(WSFederationConstants.Parameters.Result, StringComparison.Ordinal))
        {
            return true;

            //SignInResponseMessage message = WSFederationMessage.CreateFromFormPost(context.Request) as SignInResponseMessage;
            //if (message != null)
            //{
                //return true;
            //}
        }

        return base.IsValidRequestString(context, value, requestValidationSource, collectionKey, out validationFailureIndex);
    }
}   

Оригинальная вики-страница Technet теперь содержит исправленную версию кода. Решение состоит в том, чтобы явно пропустить проверку при получении сбора данных POST.

var unvalidatedFormValues = System.Web.Helpers.Validation.Unvalidated(context.Request).Form;
SignInResponseMessage message = WSFederationMessage.CreateFromNameValueCollection( WSFederationMessage.GetBaseUrl( context.Request.Url ), unvalidatedFormValues ) as SignInResponseMessage;

Validation класс принадлежит System.Web.WebPages.dll сборка, которая обычно устанавливается вместе с ASP.NET MVC.

Я не хотел связывать свою служебную библиотеку с MVC, поэтому я посмотрел в Validation класс с декомпилятором и использовал метод более низкого уровня из Microsoft.Web.Infrastructure.dll на который я уже ссылался:

var baseUrl = WSFederationMessage.GetBaseUrl(context.Request.Url);
Func<NameValueCollection> formGetter, queryStringGetter;
Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.GetUnvalidatedCollections(context, out formGetter, out queryStringGetter);
if (WSFederationMessage.CreateFromNameValueCollection(baseUrl, formGetter()) is SignInResponseMessage)
    return true;

В моем собственном валидаторе запросов (установленном в конфиге):

<httpRuntime requestValidationType="TestIdentityBroker.Service.WsFederationRequestValidator" />

Он попытался преобразовать сообщение в сообщение WIF, что вызвало функцию проверки, которая привела к... переполнению стека.

Исправлено путем обеспечения того, что поля являются полями WIF.

Другие вопросы по тегам