我正在使用 Spring Cloud Gateway 来前端遗留应用程序,以便我们可以开始在幕后迁移东西。应用程序托管的一些 url 是面向公众的,一些是设备限制的。我们控制设备,他们使用浏览器客户端访问受限制的 URL。我们在服务器上使用 tomcat 和 web.xml 中的安全约束为设备限制的 url 设置了相互身份验证:
<security-constraint>
<web-resource-collection>
<web-resource-name>Certificate Content</web-resource-name>
<!-- URL for authentication endpoint - this is locked down with the role assigned by tomcat -->
<url-pattern>/rest/secure/url1</url-pattern>
<url-pattern>/rest/secure/url2</url-pattern>
<url-pattern>/rest/secure/url3</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>certificate</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<!-- All other endpoints- force the switch from http to https with transport-guarantee -->
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Context</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>CLIENT-CERT</auth-method>
</login-config>
<security-role>
<role-name>certificate</role-name>
</security-role>
这与 tomcat 的 server.xml 中的信任库设置相结合(我可以添加它,但我认为这与此对话无关)。
我的目标是在 Spring Cloud Gateway 中实现类似的设置,该设置在后台使用响应式网络,并从遗留应用程序中删除 web.xml 限制。我想我可以将它切换到使用 tomcat 并可能让上面的 web.xml 工作,但我宁愿坚持使用响应式网络的性能优势。
关键目标:
- 只为应用部署一个 api 网关。需要相互身份验证的 url 的数量非常少,所以我宁愿不包括一个完整的其他容器来管理只是为了支持它们。
- 不要在公共 url 上要求客户端证书。
- 要求受限 URL 的有效客户端证书。
我已经设置了相互身份验证,并且可以按预期使用 need/want/none(信任库设置等),但它适用于所有 url。我还设置了 X509 安全限制,这一切似乎都有效。
我想我想要设置的是在基于路径解密 http 请求(以便我可以访问 url)之后使用 SslHandler 进行 tsl 重新协商。但是我在细节上遇到了麻烦,而且我找不到任何包含使用响应式网络进行 tsl 重新协商的 spring-boot 应用程序的示例。任何有关如何在 needClientAuth 设置为 true 的情况下重新协商 ssl 连接的提示都将不胜感激。我认为我需要使会话或其他内容无效,因为当我尝试手动执行此操作时,它似乎正在跳过协商,因为连接已在 ssl 引擎中标记为已协商。
这是我尝试过的迭代之一(这不限制 url,但我计划在我得到这个工作后添加它):
@Component
public class NettyWebServerFactoryGatewayCustomizer implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
private static final Logger LOG = LoggerFactory.getLogger(NettyWebServerFactoryGatewayCustomizer.class);
@Override
public void customize(NettyReactiveWebServerFactory serverFactory) {
serverFactory.addServerCustomizers(httpServer -> {
httpServer = httpServer.wiretap(true);
return httpServer.tcpConfiguration(tcpServer -> {
tcpServer = tcpServer.doOnConnection(connection ->
connection.addHandler("request client cert",
new SimpleChannelInboundHandler<HttpRequest>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpRequest httpRequest) {
LOG.error("HttpRequest: {}", httpRequest);
final ChannelPipeline pipeline = ctx.pipeline();
final SslHandler sslHandler = pipeline.get(SslHandler.class);
final SSLEngine sslEngine = sslHandler.engine();
sslEngine.setNeedClientAuth(true);
sslHandler.renegotiate()
.addListener(future -> ctx.fireChannelRead(httpRequest));
}
}
)
);
return tcpServer;
});
});
}
}
我看到它在调试器中执行重新协商,但它似乎仍然设置为客户端身份验证无(在 application.properties 中设置),而不是重新协商之前代码中设置的需要。我试过sslEngine.getSession().invalidate();
了,但这没有帮助。我还尝试从 ssl 提供程序生成一个新的 ssl 处理程序,但这似乎真的把事情搞砸了。
感谢您提供的任何帮助。
编辑:做更多的研究似乎这种方法不合适,因为 ssl 重新协商在 tsl 1.3 中完全被删除(参见https://security.stackexchange.com/a/230327)。有没有办法执行SSL verify client post handshake
此处描述的等效操作:https ://www.openssl.org/docs/manmaster/man3/SSL_verify_client_post_handshake.html ?
Edit2:看起来这是一个问题,我正在测试的浏览器不支持 TLS1.3 后握手。将服务器设置为只接受 TLS 1.2 似乎可行。不确定是否有更好的方法来解决这个问题,但这是我添加到我的 application.properties 中的:
server.ssl.enabled-protocols=TLSv1.2