我们的应用程序有两个部分。
- 用 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');
}