0

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

  • 用 Java 8 编写的后端,使用 Jersey 2.0 公开不同的 REST 端点。
  • UI 是使用 React 和其他节点模块构建的单页应用程序。

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

现在 UI 调用 REST 端点来显示数据。我们需要在我们的 REST API 中添加一个身份验证层,我已经在这里提出了一个单独的问题Authenticating rest endpoints and the UI using Okta

我的问题特别是我可以从 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 时为 true。它变成了一个完全不同的用户代理。

更新 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');

      }

4

1 回答 1

2

只要 REST API 是前端用于使用 Spring Security 进行身份验证的同一应用程序的一部分,它就可以访问 JSESSIONID 并因此访问包含有关已验证用户的所有信息的 Spring Security 上下文。因此不需要任何额外的身份验证机制。

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

SecurityContextHolder.getContext().getAuthentication();

使用SecurityContextPersistenceFilter其配置的存储库来获取 Authentication 对象并将其存储在 SecurityContextHolder 中。看看它默认使用的HttpContextRepository 。在那里你会发现安全上下文存储在 key 下的 HttpSession 中SPRING_SECURITY_CONTEXT,所以你也可以直接获取它。

当然,您也可以使用 Spring Security 对您的 REST API 实施身份验证和授权,就像您在前端所做的那样 - 然后一切都会为您处理。

于 2017-01-21T11:54:02.657 回答