正在对来自 UI 的 AJAX API 调用进行身份验证,后者已通过 SAML 身份验证

Authenticating AJAX API calls from the UI which is SAML authenticated

我们的应用程序有 2 个部分。

Web 界面使用 Okta 支持的 SAML 2.0 身份验证作为身份提供者。后端创建 HTTP 会话并在 cookie 中发送 JSESSIONID。

现在 UI 调用 REST 端点来显示数据。我们需要向我们的 REST API 添加一个身份验证层,我已经在此处 .

提出了一个单独的问题

我在这里的具体问题是,我可以从 UI 传递什么作为对这些 API 调用的身份验证方式,因为 UI 确实已通过身份验证并且 HTTP 会话仍在有效的。所以我不需要创建一个单独的 OAuth 2.0 令牌将其传递给 UI,以便 UI 可以将其传递回后端。 OAuth 2.0 流程对于使用我们的 REST 端点的外部客户端很有意义。

更新 1

这是我的 securityContext.xml 的摘录,它定义了两种身份验证方案:

<!-- Authenticating REST APIs -->
<security:http pattern="/rest/**" use-expressions="false">
    <security:intercept-url pattern="/nltools/**" access="IS_AUTHENTICATED_FULLY" />
    <security:custom-filter before="BASIC_AUTH_FILTER" ref="authValidationFilter" />
    <security:http-basic/>
</security:http>

<!-- SAML processing endpoints -->
<security:http pattern="/saml/**" entry-point-ref="samlEntryPoint">
    <security:custom-filter before="FIRST" ref="metadataGeneratorFilter" />
    <security:custom-filter before="CSRF_FILTER" ref="samlFilter" />
    <security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter" />
</security:http>

<!-- Secured pages with SAML as entry point -->
<security:http entry-point-ref="samlEntryPoint" use-expressions="false">
    <security:csrf />
    <security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
    <security:custom-filter before="FIRST" ref="metadataGeneratorFilter" />
</security:http>

<security:authentication-manager alias="authenticationManager">
    <security:authentication-provider ref="httpBasicAuthenticationProvider" />
    <!-- Register authentication manager for SAML provider -->
    <security:authentication-provider ref="samlAuthenticationProvider"/>
    <!-- Register authentication manager for administration UI --> 
    <security:authentication-provider>
        <security:user-service id="adminInterfaceService">
            <security:user name="admin" password="admin" authorities="ROLE_ADMIN"/>
        </security:user-service>
    </security:authentication-provider>
</security:authentication-manager> 

我不确定如何执行 SecurityContextPersistenceFilter。我应该覆盖并添加我的 /rest/** 模式的过滤器之一吗?

更新 2

这里是调用后端的JS代码(React):

  return Promise.resolve().then(() => {
      return request.post('/rest/v1/projects')
       .send(data)
       .then((success) => {
          console.log('success!', success);
          var projectName = success.body.name;
          var projectId = success.body.id;
          
          self.props.dispatch( projectActions.addNewProject(
            projectId,
            projectName
          ));
        
          self.props.dispatch( appActions.displayGoodRequestMessage( projectName + " Saved") ); 
          self.props.dispatch( projectActions.fetchProject( projectId ) ); 
          self.props.router.push('/projects');

      }

现在 JS 代码可以选择发送与该域关联的所有 cookie,这样后端可以获得 JSESSION ID cookie,但是,JS 不这样做,我认为这样做不正确做。

另一方面,如果我在浏览器中执行 https://mydomain/rest/v1/projects,只要我登录,我就会得到结果,因为这次当我的过滤器检查有效的 HTTP 会话时,它可以得到来自 request.getSession(false) 请求的会话,尽管当 JS 调用 API 时情况并非如此。它变成了一个完全不同的用户代理。

更新 3

根据@Vladimír Schäfer 的建议,我可以稍微更改上面的 JS 代码以将 cookie 作为 .withCredentials() 发送并通过后端进行身份验证,而无需执行任何特殊操作

  return Promise.resolve().then(() => {
      return request.post('/rest/v1/projects')
       .withCredentials()
       .send(data)
       .then((success) => {
          console.log('success!', success);
          var projectName = success.body.name;
          var projectId = success.body.id;
          
          self.props.dispatch( projectActions.addNewProject(
            projectId,
            projectName
          ));
        
          self.props.dispatch( appActions.displayGoodRequestMessage( projectName + " Saved") ); 
          self.props.dispatch( projectActions.fetchProject( projectId ) ); 
          self.props.router.push('/projects');

      }

只要 REST API 是 front-end 用于 Spring 安全身份验证的同一应用程序的一部分,它就可以访问 JSESSIONID,因此可以访问上下文Spring 安全性,其中包含有关经过身份验证的用户的所有信息。因此不需要任何额外的身份验证机制。

如果您在处理 Jersey 调用时执行过滤器 SecurityContextPersistenceFilter,您将能够使用以下方法访问安全上下文:

SecurityContextHolder.getContext().getAuthentication();

SecurityContextPersistenceFilter 使用其配置的存储库获取身份验证对象并将其存储在 SecurityContextHolder 中。看看它默认使用的 HttpContextRepository 。在那里你会发现security context是存储在keySPRING_SECURITY_CONTEXT下的HttpSession,所以你也可以直接fetch它

当然,您也可以使用 Spring 安全性在您的 REST API 上强制执行身份验证和授权,就像您在 front-end 上所做的那样 - 然后一切都会为您处理.