如何配置Apache Tomcat,使其请求客户端凭证但不对用户进行身份验证?
Absolutely! You can configure Apache Tomcat to capture client-provided credentials without enforcing full user authentication. This is a common scenario when you need to collect credential data (like client certificates or HTTP Basic Auth details) for logging, auditing, or custom processing—without requiring Tomcat to validate the user's identity against a backend store.
Here’s how to set this up for the two most common credential types:
If you’re looking to capture client SSL/TLS certificates without verifying their validity or the user’s identity:
1. Configure the SSL Connector to Request Certificates
Edit your conf/server.xml file to update the HTTPS Connector. Use clientAuth="want" to ask clients for a certificate without enforcing it, and optionally bypass validation if you want to accept untrusted certificates:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="150" SSLEnabled="true"> <SSLHostConfig> <Certificate certificateKeystoreFile="conf/localhost-rsa.jks" type="RSA" /> <!-- Request client certificates but don't enforce validation --> <ClientAuth want="true" /> <!-- Optional: Use a truststore that accepts all certificates (for testing only!) --> <!-- <Truststore truststoreFile="conf/empty-truststore.jks" truststorePassword="changeit" /> --> </SSLHostConfig> </Connector>
clientAuth="want": Tomcat will prompt clients for a certificate, but won’t reject connections if none is provided.- Important: By default, Tomcat will validate client certificates against its truststore. If you need to accept untrusted certificates (e.g., self-signed ones), create an empty truststore or use a custom SSL context that skips validation (note: this is not recommended for production unless you have alternative security controls).
2. Extract Certificate Data in Your Application
Once the connector is set up, you can retrieve the client certificate in your web app via the request object. For example, in a Servlet:
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.security.cert.X509Certificate; public class CredentialCaptureServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) { // Retrieve the client certificate array X509Certificate[] certs = (X509Certificate[]) request.getAttribute( "javax.servlet.request.X509Certificate" ); if (certs != null && certs.length > 0) { X509Certificate clientCert = certs[0]; // Extract useful details String subjectDN = clientCert.getSubjectDN().getName(); String issuerDN = clientCert.getIssuerDN().getName(); String serialNumber = clientCert.getSerialNumber().toString(); // Log or process the data as needed System.out.println("Captured client certificate subject: " + subjectDN); } } }
If you want to capture username/password from HTTP Basic Auth headers without validating the user:
1. Use a Custom Filter to Capture Credentials
Instead of enabling Tomcat’s built-in Basic Authentication (which enforces validation), create a custom Filter to intercept the Authorization header and extract credentials:
import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Base64; public class BasicAuthCaptureFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpReq = (HttpServletRequest) request; String authHeader = httpReq.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Basic ")) { // Decode the Base64-encoded credentials String base64Credentials = authHeader.substring("Basic ".length()).trim(); String credentials = new String( Base64.getDecoder().decode(base64Credentials), StandardCharsets.UTF_8 ); // Split into username and password String[] parts = credentials.split(":", 2); if (parts.length == 2) { String username = parts[0]; String password = parts[1]; // Log or process the credentials (no validation here) System.out.println("Captured Basic Auth username: " + username); } } // Pass the request through to the next filter/servlet chain.doFilter(request, response); } // Implement init() and destroy() methods as needed }
2. Register the Filter in web.xml
Add the filter configuration to your app’s WEB-INF/web.xml to apply it to your desired URLs:
<filter> <filter-name>BasicAuthCaptureFilter</filter-name> <filter-class>com.yourpackage.BasicAuthCaptureFilter</filter-class> </filter> <filter-mapping> <filter-name>BasicAuthCaptureFilter</filter-name> <url-pattern>/*</url-pattern> <!-- Apply to all URLs --> </filter-mapping>
- Security Consideration: Capturing credentials without authentication means you’re not enforcing access control. Ensure this aligns with your security policies—never use this as a substitute for proper authentication if your application requires it.
- Production vs. Testing: Bypassing certificate validation (for client certs) should only be done in testing environments. In production, use a truststore that only includes trusted CA certificates, or implement custom validation logic in your app.
内容的提问来源于stack exchange,提问作者user1289

