4

我有一些電話會針對我使用開箱即用的IdentityServer STS進行保護的ServiceStack REST服務進行調用。通過Thinktecture的IdentityServer STS確保REST端點安全的AJAX呼叫

我正在對REST端點進行AJAX調用,我不確定如何設置登錄過程來獲取安全令牌。 REST端點與撥打電話的網站位於不同的域。到目前爲止,我發現的信息似乎都圍繞着客戶端對受保護資源進行調用的過程獲得302重定向到身份服務器登錄頁面,然後在成功身份驗證後獲得302重定向到領域或根據配置進行回覆。我已經將所有這些都正確地吸引過來了,如果我只是瀏覽REST服務,它會很好用。然而,對於我的網絡應用程序,AJAX和302s並不是最好的朋友,所以理想情況下我想要的是來自同一個ServiceStack網站的REST端點,它接受用戶名和密碼並返回安全令牌任何重定向的複雜性(我將處理web應用程序本身中的401重定向,當我關閉web.config中的passiveRedirectEnabled時,我會得到這些重定向。有關如何使用IdentityServer實現此目的的任何想法?

乾杯, Clint。

回答

5

完成與充分的休息端點答案:

在ServiceStack Web應用程序:

路線在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; } 
} 

而響應DT O

響應DTO只需要處理POST - 是的,您可以將URL /密碼 作爲參數添加到GET請求的URL中,但聽起來並不像推薦的那樣。 實際上,您通常可能會將此信息放在HTTP請求 的Authorization標頭中,但這會使您在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應用程序的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> 

最後,驗證與REST端點,POST用戶名和密碼到一個簡單的JavaScript客戶端應用程序登錄servicestackwebapp的端點,然後當您收到響應時,將其發回到領域 - 這樣做會爲當前會話設置FedAuth cookie,因此您不必考慮令牌管理客戶端了。

$.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 ......我希望我以前發現了它,但它令人欣慰的知道我實現了類似於微軟例子的東西 - 我們分歧的指向他們轉換爲簡單網絡令牌的地方 - 我將它保留爲SAML令牌,並將其傳遞給客戶端(序列化)。

2

我到目前爲止的解決方案:

我已經暴露的REST服務,使對WS-信託端點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(); 
     } 
    } 

我相信唯一剩下的一點就是建立FedAuth cookie,我相信這些cookie是在安全令牌的第一個帖子上設置到安全的web應用程序。

請權衡任何改進或建議。謝謝!