【问题标题】:AJAX call against REST endpoint secured with Thinktecture's IdentityServer STS针对 REST 端点的 AJAX 调用由 Thinktecture 的 IdentityServer STS 保护
【发布时间】:2014-02-28 12:08:01
【问题描述】:

我遇到了一些针对 ServiceStack REST 服务的调用,我已使用开箱即用的 IdentityServer STS 保护了该服务。

我正在对 REST 端点进行 AJAX 调用,但我不确定如何设置登录过程以获取要传递的安全令牌。 REST 端点与进行调用的网站位于不同的域中。到目前为止,我发现的信息似乎都围绕着客户端调用受保护资源获得 302 重定向到身份服务器登录页面,然后在成功身份验证后获得 302 重定向到领域或根据配置回复。我已经正确连接了所有这些,如果我只是浏览 REST 服务,它会很好用。然而,关于我的网络应用程序,AJAX 和 302 并不是最好的朋友,所以理想情况下,我想我想要的是来自同一个 ServiceStack 网站的 REST 端点,它接受用户名和密码并返回一个安全令牌,而无需任何重定向的复杂性(我将在我的 web 应用程序本身中处理 401 重定向,当我在 web.config 中关闭passiveRedirectEnabled 时会得到)。关于如何使用 IdentityServer 实现这一点有什么想法吗?

干杯, 克林特。

【问题讨论】:

    标签: ajax rest servicestack federated-identity thinktecture-ident-server


    【解决方案1】:

    使用完整的 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; }
    }
    

    以及响应 DTO

    响应 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.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 端点对简单的 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 ...我希望我以前找到过它,但很高兴知道我已经实现了类似于 Microsoft 示例的东西 - 我们在它们转换为简单 Web 令牌的点上存在分歧 - 我将其保留为 SAML 令牌和将其(序列化)传递给客户端。

    【讨论】:

      【解决方案2】:

      到目前为止我的解决方案:

      我在 REST 服务上公开了一个端点,该端点调用 IdentityServer 默认提供的 WS-Trust 端点。在 .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,我相信它是在安全令牌的第一次发布到安全 Web 应用程序时设置的。

      请权衡任何改进或建议。谢谢!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-08-04
        • 1970-01-01
        • 2014-08-01
        • 2014-09-25
        • 2016-03-04
        • 2016-05-03
        • 2020-08-27
        • 2015-05-02
        相关资源
        最近更新 更多