4

У меня есть некоторые драм, которые вызывают вызов службы ServiceStack REST, которую я защитил с помощью кода IdentityServer STS.AJAX вызов с конечной точкой REST, защищенной с помощью IdentityServer Thinktecture STS

Я делаю вызов AJAX против конечной точки REST, и я не уверен, как можно настроить процедуру входа в систему, чтобы получить маркер безопасности. Конечная точка REST находится в другом домене, чем веб-сайт, делающий вызов. Информация, которую я обнаружил до сих пор, все, похоже, вращается вокруг процедуры, когда клиент делает вызов защищенного ресурса, получает перенаправление 302 на страницу входа в систему идентификации, а затем после успешной аутентификации получает перенаправление 302 либо в область, либо в ответ в зависимости от конфигурации. Я правильно зацепил все это, и он отлично работает, если я просто просматриваю службы REST. Однако в отношении моего веб-приложения AJAX и 302s не совсем лучшие друзья, поэтому в идеале я бы хотел, чтобы я был конечной точкой REST с того же веб-сайта ServiceStack, который берет имя пользователя и пароль и возвращает маркер безопасности без усложнение любых переадресаций (я буду обрабатывать перенаправления 401 в самом своем веб-приложении, которое я получу, когда отключу passiveRedirectEnabled в web.config). Любые мысли о том, как можно добиться этого с помощью IdentityServer?

Cheers, Клинт.

ответ

5

Завершение ответ с полным REST конечной точки:

В ServiceStack веб-приложение:

пути к конечной точке входа в AppHost.cs с чем-то вроде:

public override void Configure(Container container) 
{ 
    Routes.Add<Logon>("/logon", "POST"); 
} 

Тогда есть простое имя пользователя/пароль Запрос DTO

public class Logon 
{ 
    public string UserName { get; set; } 
    public string Password { get; set; } 
} 

И соответственно onse DTO

Ответ DTO должен обрабатывать только POST - да, вы можете добавить URL/пароль в качестве параметров URL-адреса для запроса GET, но это не похоже на то, что это рекомендуется. Фактически, вы, вероятно, обычно помещаете эту информацию в заголовок авторизации HTTP-запроса , но это затрудняет вашу работу в ServiceStack.

public class LogonService : Service 
{ 
    public object Post(Logon request) 
    { 
     var securityToken = GetSaml2SecurityToken(request.UserName, request.Password, "https://myserver/identityserverwebapp/issue/wstrust/mixed/username", "http://myserver/servicestackwebapp/"); 

     return SerializeRequestSecurityTokenResponse(securityToken); 
    } 

    private RequestSecurityTokenResponse GetSaml2SecurityToken(string username, string password, string endpointAddress, string realm) 
    { 
     var factory = new WSTrustChannelFactory(new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), 
      new EndpointAddress(endpointAddress)) 
     { 
      TrustVersion = TrustVersion.WSTrust13 
     }; 

     factory.Credentials.UserName.UserName = username; 
     factory.Credentials.UserName.Password = password; 

     var channel = (WSTrustChannel)factory.CreateChannel(); 
     RequestSecurityTokenResponse requestSecurityTokenResponse; 

     channel.Issue(new RequestSecurityToken 
     { 
      TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0", 
      AppliesTo = new EndpointReference(realm), 
      RequestType = RequestTypes.Issue, 
      KeyType = KeyTypes.Bearer, 
     }, out requestSecurityTokenResponse); 

     return requestSecurityTokenResponse; 
    } 

    private string SerializeRequestSecurityTokenResponse(RequestSecurityTokenResponse requestSecurityTokenResponse) 
    { 
     var serializer = new WSTrust13ResponseSerializer(); 
     var context = new WSTrustSerializationContext(FederatedAuthentication.FederationConfiguration.IdentityConfiguration.SecurityTokenHandlerCollectionManager); 
     var stringBuilder = new StringBuilder(128); 

     using (var writer = XmlWriter.Create(new StringWriter(stringBuilder), new XmlWriterSettings { OmitXmlDeclaration = true})) 
     { 
      serializer.WriteXml(requestSecurityTokenResponse, writer, context); 
      writer.Flush(); 
      return stringBuilder.ToString(); 
     } 
    } 
} 

ServiceStack веб-приложение Web.config должен выглядеть довольно похож на это:

<?xml version="1.0"?> 
<configuration> 
    <configSections> 
    <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" /> 
    <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" /> 
    </configSections> 
    <location path="FederationMetadata"> 
    <system.web> 
     <authorization> 
     <allow users="*" /> 
     </authorization> 
    </system.web> 
    </location> 
    <!-- to allow the logon route without requiring authentication first. --> 
    <location path="logon"> 
    <system.web> 
     <authorization> 
     <allow users="*" /> 
     </authorization> 
    </system.web> 
    </location> 
    <system.web> 
    <httpHandlers> 
     <add path="*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" /> 
    </httpHandlers> 
    <compilation debug="true" /> 
    <authentication mode="None" /> 
    <authorization> 
     <deny users="?" /> 
    </authorization> 
    <httpRuntime targetFramework="4.5" requestValidationMode="4.5" /> 
    </system.web> 
    <system.webServer> 
    <modules runAllManagedModulesForAllRequests="true"> 
     <add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" /> 
     <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" /> 
    </modules> 
    <validation validateIntegratedModeConfiguration="false" /> 
    <handlers> 
     <add path="*" name="ServiceStack.Factory" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" preCondition="integratedMode" resourceType="Unspecified" allowPathInfo="true" /> 
    </handlers> 
    </system.webServer> 
    <system.identityModel> 
    <identityConfiguration> 
     <audienceUris> 
     <add value="http://myserver/servicestackwebapp/" /> 
     </audienceUris> 
     <issuerNameRegistry type="System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <trustedIssuers> 
      <add thumbprint="B6E05E14243FB7D76D5B660532520FB94679AA01" name="http://mycertificatefriendlyname" /> 
     </trustedIssuers> 
     </issuerNameRegistry> 
     <certificateValidation certificateValidationMode="None" /> 
     <securityTokenHandlers> 
     <securityTokenHandlerConfiguration saveBootstrapContext="true" /> 
     </securityTokenHandlers> 
    </identityConfiguration> 
    </system.identityModel> 
    <system.identityModel.services> 
    <federationConfiguration> 
     <cookieHandler requireSsl="false" /> 
     <wsFederation passiveRedirectEnabled="false" issuer="https://myserver/identityserverwebapp/issue/wsfed" realm="http://myserver/servicestackwebapp/" requireHttps="false" /> 
    </federationConfiguration> 
    </system.identityModel.services> 
</configuration> 

И, наконец, для проверки подлинности простого клиента приложения Javascript с REST конечной точкой, POST именем пользователя и паролем к конечной точки входа в систему servicestackwebapp, а затем, когда вы получите ответ, отправьте это обратно в область - таким образом настройте файлы cookie FedAuth для вашего текущего сеанса, поэтому вам больше не нужно думать о клиентской стороне управления токеном.

$.ajax({ 
    type: "POST", 
    url: "/servicestackwebapp/logon", 
    dataType: "text", 
    data: { UserName: "myuser", Password: "mypassword" }, 
    success: function (data) { 
     $.ajax({ 
      type: "POST", 
      url: "/servicestackwebapp/", 
      data: "wa=wsignin1.0&wresult=" + encodeURIComponent(data) 
     }); 
    } 
}); 

Кроме того, следует отметить, что все HTTP оконечных выше следует вместо этого идти через HTTPS - не глупо, как я сделал в моем примере и отправить открытым текстом претензии через HTTP.

Также после того, как я реализовал свое решение, я нашел это: http://msdn.microsoft.com/en-us/library/hh446531.aspx ... Хотел бы я найти его раньше, но я уверен, что я реализовал нечто похожее на пример Microsoft - мы расходимся в где они конвертируются в простой веб-токен - я сохраняю его как токен SAML и передаю его (сериализованному) клиенту.

2

Мое решение до сих пор:

Я выставил конечную точку на REST сервис, который делает вызов против WS-Trust конечной точке, которая обеспечивает IdentityServer по умолчанию. В .NET 4.5, вам необходимо иметь ссылку на Thinktecture.IdentityModel, как UserNameWSTrustBinding не доступен в System.IdentityModel см: What's the .NET 4.5 equivalent to UserNameWSTrustBinding?

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

private SecurityToken GetSamlSecurityToken(string username, string password, string endpointAddress, string realm) 
    { 
     var factory = new WSTrustChannelFactory(new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), 
      new EndpointAddress(endpointAddress)) 
     { 
      TrustVersion = TrustVersion.WSTrust13 
     }; 

     factory.Credentials.UserName.UserName = username; 
     factory.Credentials.UserName.Password = password; 

     var channel = factory.CreateChannel(); 

     var securityToken = channel.Issue(new RequestSecurityToken 
     { 
      TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0", 
      AppliesTo = new EndpointReference(realm), 
      RequestType = RequestTypes.Issue, 
      KeyType = KeyTypes.Bearer, 
     }); 

     return securityToken; 
    } 

Это будет аутентифицировать на основе имени пользователя и пароля, а параметр EndpointAddress будет выглядеть примерно так:

https://myserver/identityserverapp/issue/wstrust/mixed/username 

Затем я сериализации маркер безопасности следующим образом:

private string SerializeSecurityToken(SecurityToken securityToken) 
    { 
     var serializer = new WSSecurityTokenSerializer(); 
     var stringBuilder = new StringBuilder(); 

     using (var writer = XmlWriter.Create(new StringWriter(stringBuilder))) 
     { 
      serializer.WriteToken(writer, securityToken); 
      return stringBuilder.ToString(); 
     } 
    } 

Я считаю, что единственный оставшийся бит устанавливает файлы cookie FedAuth, которые, как я полагаю, установлены на первом столбце маркера безопасности для защищенного веб-приложения.

Пожалуйста, взвешивайте с любыми улучшениями или предложениями. Благодаря!