代码之家  ›  专栏  ›  技术社区  ›  Stefan Eichelmann

Azure中的Spring Boot-请求标头中的客户端证书

  •  2
  • Stefan Eichelmann  · 技术社区  · 7 年前

    我们目前在Spring Boot应用程序中实现了相互认证,需要在Azure中部署。 Azure的loadbalancer重定向请求头字段“X-ARR-ClientCert”中的客户端证书(Base64编码),Spring无法在那里找到它。

    microsoft文档展示了如何在.NET应用程序中处理此问题: https://docs.microsoft.com/en-gb/azure/app-service-web/app-service-web-configure-tls-mutual-auth

    我试图从OncePerRequestFilter中的头中提取证书,并将其设置为如下请求:

    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;
        }
    }
    

    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());
    }
    
    1 回复  |  直到 7 年前
        1
  •  3
  •   Stefan Eichelmann    7 年前

    我应该更仔细地阅读例外情况:

    sun.security.x509.X509CertImpl cannot be cast to [Ljava.security.cert.X509Certificate; /oaa/v1/spaces
    

    我必须像这样设置一系列证书:

    httpServletRequest.setAttribute(JAVAX_SERVLET_REQUEST_X509_CERTIFICATE, new X509Certificate[]{x509Certificate});