Добрый день.
Сегодня мы рассмотрим такую возможность Spring security, как аутентификация пользователей с помощью TLS сертификата. Так называемая mutual authentication.
Для начала сгенерируем сертификаты для клиента и сервера. Примеров того, как это сделать в интернете достаточно, приведём один из них.
Генерируем сертификат сервера
Генерируем RSA ключ сервера
openssl genrsa -aes256 -out serverprivate.key 2048
Генерируем CA сертификат с использоваие данного ключа. Большинство полей можно заполнить на ваше усмотрение, кроме поля CN, который должен быть равен домену, который мы хотим защитить. В нашем случае - это localhost.
openssl req -x509 -new -nodes -key serverprivate.key -sha256 -days 1024 -out serverCA.crt
Импортируем сертификат в Java truststore. Задаём дефолтный пароль changeit
keytool -import -file serverCA.crt -alias serverCA -keystore truststore.jks
Экспортируем сертификат в keystore, пароль снова changeit
openssl pkcs12 -export -in serverCA.crt -inkey serverprivate.key -certfile serverCA.crt -out keystore.p12
Генерируем клиентский сертификат.
Создаём приватный ключ и запрос на получение сертификата. В поле CN указываем имя пользвателя. В нашем случае CN='Alex.Pashnin'.
openssl genrsa -aes256 -out clientprivate.key 2048
openssl req -new -key clientprivate.key -out client.csr
Создаём сертификат.
openssl x509 -req -in client.csr -CA serverCA.crt -CAkey serverprivate.key -CAcreateserial -out client.crt -days 365 -sha256
 
Перейдём к созданию тестового приложения
Воспользуемся для этого gradle.
gradle.build
//Плангин для быстрого старта Spring Boot приложений. Работает только с gradle версии 4.4+.
//В случае проблем с версией закомментировать, выполнить gradle wrapper --gradle-version=4.10.2
//и раскомментировать обратно
plugins {
    id 'org.springframework.boot' version '2.1.0.RELEASE'
}
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
repositories {
    jcenter()
    mavenCentral()
}
dependencies {
    compile 'org.slf4j:slf4j-api:1.7.21'
    compile 'org.springframework.boot:spring-boot-starter-security:2.1.0.RELEASE'
    compile 'org.springframework.boot:spring-boot-starter-web:2.1.0.RELEASE'
}
 
Далее создадим главный класс приложения
@EnableWebSecurity
@SpringBootApplication
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class CertAuthServer extends WebSecurityConfigurerAdapter {
    public static void main(String[] args) {
        SpringApplication.run(CertAuthServer.class, args);
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated()
                .and().csrf().disable()
                .x509()
                .subjectPrincipalRegex("CN=(.*?),")
                .userDetailsService(userDetailsService());
    }
    @Bean
    public UserDetailsService userDetailsService() {
        return new UserDetailsService() {
            @Override
            public UserDetails loadUserByUsername(String username) {
                if (username.equals("Alex.Pashnin")) {
                    return new User(username, "",
                            AuthorityUtils
                                    .commaSeparatedStringToAuthorityList("ROLE_USER"));
                }
                return null;
            }
        };
    }
}
Создадим простой веб котроллер, который будет приветствовать авторизованного пользователя.
@RestController
@RequestMapping(value = "/test")
public class TestController {
    @PreAuthorize("hasAuthority('ROLE_USER')")
    @GetMapping("/hello")
    public String hello(Principal principal) {
        UserDetails currentUser
                = (UserDetails) ((Authentication) principal).getPrincipal();
        return "Hello " + currentUser.getUsername()+"!";
    }
}
Конфигурационный файл application.properties
server.ssl.key-store = classpath:keystore.p12
server.ssl.trust-store = classpath:truststore.jks
server.ssl.trust-store-password = changeit
server.ssl.key-store-password = changeit
server.ssl.key-password = changeit
server.ssl.protocol = TLS
server.ssl.key-alias = 1
server.ssl.client-auth = need
server.ssl.enabled = true
server.port = 8443
Поскольку я не задал алиас для серверного ключа, посмотрим его в хранилище. Необходимое нам поле - Alias name, его и задаём в server.ssl.key-alias.
$ keytool -v -list -keystore keystore.p12 
Enter keystore password:  
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
Alias name: 1
Creation date: Nov 12, 2018
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=localhost, OU=dev, O=tune-it, ST=Some-State, C=RU
Issuer: CN=localhost, OU=dev, O=tune-it, ST=Some-State, C=RU
ВАЖНО!
Для корректной работы данного приложения, отсутсвия неожиданных логов логов из разряда
 o.s.s.w.a.p.x.X509AuthenticationFilter   : No client certificate found in request. 
следует отключить csrf и добавить server.ssl.client-auth = need в application.properties
 
Запускаем наше приложение
$ gradle wrapper
$ ./gradlew bootRun
Убеждаемся в его работоспособности
$ curl -k --cert client.crt --key clientprivate.key -X GET 'https://localhost:8443/test/hello'
Hello Alex.Pashnin!
 
Таким, довольно нехитрым образом, мы произвели авторизацию и аутентификацию пользователя с помощью TLS сертификата. На этом всё, спасибо за внимание.