问题描述
我们当前在Spring Boot应用程序中实现了相互身份验证,需要将其部署在Azure中.Azure的负载平衡器在请求标头字段"X-ARR-ClientCert"中重定向客户端证书(Base64编码),而Spring无法在该位置找到它.=>身份验证失败
We currently implemented mutual authentication in our Spring Boot application and need to deploy it in Azure.Azure's loadbalancer redirects the client certificate (Base64 encoded) in the request header field "X-ARR-ClientCert" and Spring is not able to find it there.=> Authentication fails
Microsoft文档显示了如何在.NET应用程序中处理此问题: https://docs.microsoft.com/zh-CN/azure/app-service-web/app-service-web-configure-tls-mutual-auth
The microsoft documentation shows how to handle this in a .NET application: https://docs.microsoft.com/en-gb/azure/app-service-web/app-service-web-configure-tls-mutual-auth
我试图从OncePerRequestFilter的标头中提取证书,并将其设置为这样的请求:
I tried to extract the certificate from the header in an OncePerRequestFilter and set it to the request like this:
public class AzureCertificateFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory.getLogger(AzureCertifacteFilter.class);
private static final String AZURE_CLIENT_CERTIFICATE_HEADER = "X-ARR-ClientCert";
private static final String JAVAX_SERVLET_REQUEST_X509_CERTIFICATE = "javax.servlet.request.X509Certificate";
private static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----\n";
private static final String END_CERT = "\n-----END CERTIFICATE-----";
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
X509Certificate x509Certificate = extractClientCertificate(httpServletRequest);
// azure redirects the certificate in a header field
if (x509Certificate == null && StringUtils.isNotBlank(httpServletRequest.getHeader(AZURE_CLIENT_CERTIFICATE_HEADER))) {
String x509CertHeader = BEGIN_CERT + httpServletRequest.getHeader(AZURE_CLIENT_CERTIFICATE_HEADER) + END_CERT;
try (ByteArrayInputStream certificateStream = new ByteArrayInputStream(x509CertHeader.getBytes())) {
X509Certificate certificate = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(certificateStream);
httpServletRequest.setAttribute(JAVAX_SERVLET_REQUEST_X509_CERTIFICATE, certificate);
} catch (CertificateException e) {
LOG.error("X.509 certificate could not be created out of the header field {}. Exception: {}", AZURE_CLIENT_CERTIFICATE_HEADER, e.getMessage());
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
private X509Certificate extractClientCertificate(HttpServletRequest request) {
X509Certificate[] certs = (X509Certificate[]) request.getAttribute(JAVAX_SERVLET_REQUEST_X509_CERTIFICATE);
if (certs != null && certs.length > 0) {
LOG.debug("X.509 client authentication certificate:" + certs[0]);
return certs[0];
}
LOG.debug("No client certificate found in request.");
return null;
}
}
但是这在Spring过滤器链中稍后会失败,但以下情况除外:
But this fails later in the Spring filter chain with the following exception:
sun.security.x509.X509CertImpl cannot be cast to [Ljava.security.cert.X509Certificate; /oaa/v1/spaces
配置如下:
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("**/docs/restapi/**").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.disable()
.addFilterBefore(new AzureCertificateFilter(), X509AuthenticationFilter.class)
.x509()
.subjectPrincipalRegex("CN=(.*?)(?:,|$)")
.userDetailsService(userDetailsService());
}
推荐答案
我应该更仔细地阅读该异常:
I should have read the exception more carefully:
sun.security.x509.X509CertImpl cannot be cast to [Ljava.security.cert.X509Certificate; /oaa/v1/spaces
我必须像这样设置一系列证书:
I had to set an array of certificates like this:
httpServletRequest.setAttribute(JAVAX_SERVLET_REQUEST_X509_CERTIFICATE, new X509Certificate[]{x509Certificate});
这篇关于Azure中的Spring Boot-请求标头中的客户端证书的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!