重定向除Ajax/Javascript以外的方式发送认证请求,避免CORS错误

Redirect way except Ajax/Javascript to send the authentication request to avoid CORS error

问题描述:

假设我有一个 Web 应用程序 (Java+Saml2.0),它有一个登录按钮来调用 ADFS(一种身份提供程序)进行身份验证。

它正在使用 Java 脚本(Ajax 有同样的问题)调用 ADFS 2016 的一个端点。由于请求如果从 Javascript/Ajax 发送,浏览器会抛出 CORS 错误。 (ADFS2016服务器端不支持修改CORSheader/response/origins)

而且我从某人那里听说避免 CORS 错误的一种方法是使用重定向而不是使用 Ajax/Javascript 直接调用一个 URL/endpoint。

有人可以对这种情况给出一些见解吗?如何修改我的代码以执行此类重定向而不导致 CORS?

PS:我不想降低浏览器安全级别来绕过 CORS,我也不想升级到 ADFS2019,尽管它支持自定义 CORS 来源。

我不确定您是不是想像这样使用 javascript 来实现某些特殊情况。但通常在使用 SAML 对用户进行身份验证时,您会从后端发出 HTTP 重定向作为对用户单击登录按钮的响应。

要理解这一切,首先了解我在 this post 中经历的 SAML 身份验证流程很重要。

  1. 用户通过导航到受保护的页面或在本例中单击按钮来触发身份验证
  2. 应用程序,或 SAML 中的服务提供商 (SP),构建 SAML 身份验证请求并通过将其作为 URL 参数添加并向用户发送后端 HTTP 重定向将其发送至 IdP。也可以使用 HTTP POST explained here
  3. 发送身份验证请求
  4. IdP 以其认为合适的方式对用户进行身份验证。
  5. IdP 使用 HTTP POST 连同 SAML 响应和 SAML 断言将用户发送回 SP。这包含身份验证的结果以及有关用户的任何额外信息。
  6. SP,您的应用程序,解释 SAML 响应并让用户访问受保护的应用程序。 此重定向在 URL 中包含编码的 SAML 身份验证请求,该请求由 ADFS 解析以了解身份验证请求的来源以及如何对用户进行身份验证。

有几个库和框架可用于管理 SAML 流量,包括使用重定向或其他方法发送消息。如果您使用 Java,Spring 将 SAML 管理作为 part of their security framework. Another library is the PAC4J which provides a SAML module

虽然这两种方法都适用于构建 SP 以与现有 IdP 集成的最一般用例,但如果您需要执行更多自定义案例或在您自己的 OpenSAML 上构建 IdP,则可以作为替代方案。 OpenSAML 是用于处理 SAML 的更底层的库。 In this write up on my blog 我展示了如何使用来自 OpenSAML 的重定向构建和发送 SAML 身份验证请求。

下面是使用 OpenSAML 的简化示例。有关完整示例,请参阅 sample code here and here

身份验证请求是使用 OpenSAML 构建的

AuthnRequest authnRequest = OpenSAMLUtils.buildSAMLObject(AuthnRequest.class);
authnRequest.setIssueInstant(Instant.now());
authnRequest.setDestination(IPD_SSO_DESTINATION);
authnRequest.setProtocolBinding(SAMLConstants.SAML2_ARTIFACT_BINDING_URI);
authnRequest.setAssertionConsumerServiceURL(SP_ASSERTION_CONSUMER_SERVICE_URL);
authnRequest.setID(RANDOM_ID);
authnRequest.setIssuer(ISSUER);
authnRequest.setNameIDPolicy(NAME_ID_POLICY);

将消息添加到消息上下文并设置目标

MessageContext context = new MessageContext();
context.setMessage(authnRequest);

SAMLPeerEntityContext peerEntityContext =     context.getSubcontext(SAMLPeerEntityContext.class, true);
SAMLEndpointContext endpointContext =     peerEntityContext.getSubcontext(SAMLEndpointContext.class, true);
endpointContext.setEndpoint(MESSAGE_RECEIVER_ENDPOINT);

使用 HTTP 重定向发送消息

HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder();
encoder.setMessageContext(context);
encoder.setHttpServletResponse(httpServletResponse);

encoder.initialize();
encoder.encode();

对于那些想深入挖掘的人,我写了一本关于使用 OpenSAML 的书,A Guide to OpenSAML, as well as book on SAML as a framework, SAML 2.0: Designing secure identity federation.

我在 my blog

上也有大量资源