I ran into a similar problem and thought Id was using the solution I came up with.
First, you need to understand that SSL certificate authentication will be processed on the side of your web server (cfr. Durs explanation, with the setting "clientAuth = want"). Then your web application should be configured to process the provided (and allowed) certificate, matching it with the user, etc.
The little difference I have is that Im packs my spring boot application into a WAR archive, which is then deployed to an existing Tomcat application server.
The My Tomcats server.xml configuration file defines the HTTPS connector as follows:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" keystoreFile="/opt/tomcat/conf/key-stores/ssl-keystore.jks" keystorePass="some-complex-password" clientAuth="want" sslProtocol="TLS" truststoreFile="/opt/tomcat/conf/trust-stores/ssl-truststore.jks" truststorePass="some-other-complex-password" />
A small comment to avoid confusion: keystoreFile contains a pair of certificates / private keys used for SSL (only), and truststoreFile contains allowed CA certificates for SSL authentication for clients (note that you can also add client certificates directly to this trust - store).
If you use the tomcat built-in container with your spring boot application, you can configure these parameters in your application properties file using the following property key / values:
server.ssl.key-store=/opt/tomcat/conf/key-stores/ssl-keystore.jks server.ssl.key-store-password=some-complex-password server.ssl.trust-store=/opt/tomcat/conf/trust-stores/ssl-truststore.jks server.ssl.trust-store-password=some-other-complex-password server.ssl.client-auth=want
Then, in my web application, I declare a specific SSL configuration as follows:
@Configuration @EnableWebSecurity //In order to use @PreAuthorise() annotations later on... @EnableGlobalMethodSecurity(prePostEnabled = true) public class SSLAuthConfiguration extends WebSecurityConfigurerAdapter { @Value("${allowed.user}") private String ALLOWED_USER; @Value("${server.ssl.client.regex}") private String CN_REGEX; @Autowired private UserDetailsService userDetailsService; @Override protected void configure (final HttpSecurity http) throws Exception { http .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/url-path-to-protect").authenticated() //Specify the URL path(s) requiring authentication... .and() .x509() //... and that x509 authentication is enabled .subjectPrincipalRegex(CN_REGEX) .userDetailsService(userDetailsService); } @Autowired //Simplified case, where the application has only one user... public void configureGlobal (final AuthenticationManagerBuilder auth) throws Exception { //... whose username is defined in the application properties. auth .inMemoryAuthentication() .withUser(ALLOWED_USER).password("").roles("SSL_USER"); }
}
Then I need to declare a UserDetailsService bean (for example, in my main application class):
@Value("${allowed.user}") private String ALLOWED_USER; @Bean public UserDetailsService userDetailsService () { return new UserDetailsService() { @Override public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { if (username.equals(ALLOWED_USER)) { final User user = new User(username, "", AuthorityUtils.createAuthorityList("ROLE_SSL_USER")); return user; } return null; } }; }
And here it is! Then I can add @PreAuthorize ("hasRole ('ROLE_SSL_USER") annotations to the methods that I want to protect.
To summarize, the authentication flow will look like this:
- The user provides an SSL certificate;
- Tomcat checks its trust store;
- The custom WebSecurityConfigurerAdapter extracts the "username" from the CN certificates;
- The application authenticates the user associated with the received username;
- At the method level, if annotated with @PreAuthorize ("hasRole ('SSL_USER')"), the application checks to see if the user has the required role.