Je crée des services Web reposants et j'utilise Spring-Boot pour créer un conteneur Tomcat intégré.
L'une des exigences est que cela implémente SSL à 2 voies. J'ai regardé l'objet HttpSecurity et je peux l'obtenir pour exécuter uniquement les services Web sur un canal SSL en utilisant ceci: -
@Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("CONFIGURED");
http
// ...
.requiresChannel()
.anyRequest().requiresSecure();
}
Ce que je n'arrive pas à trouver, c'est un moyen de rendre le service Web uniquement accessible aux applications fournissant un certificat client valide.
Je n'ai qu'une connaissance de base de SSL, donc même un pointeur général dans la bonne direction serait apprécié.
Le serveur sur lequel est déployé aura un mélange d'applications - c'est le seul qui doit être verrouillé avec SSL bidirectionnel. Ce que je recherche vraiment, c'est un moyen de verrouiller une seule application pour n'accepter que les certificats clients.
Vous pouvez configurer clientAuth=want
, voir Référence de configuration d'Apache Tomcat 8 :
Définissez sur
true
si vous souhaitez que la pile SSL nécessite une chaîne de certificats valide du client avant d'accepter une connexion. Définissez surwant
si vous souhaitez que la pile SSL demande un certificat client, mais n'échoue pas si aucun n'est présenté. Une valeurfalse
(qui est la valeur par défaut) ne nécessitera pas de chaîne de certificats sauf si le client demande une ressource protégée par une contrainte de sécurité qui utiliseCLIENT-CERT
authentification.
puis lisez le certificat client avec Spring Security - X.509 Authentication :
Vous pouvez également utiliser SSL avec une "authentification mutuelle"; le serveur demandera alors un certificat valide au client dans le cadre de la prise de contact SSL. Le serveur authentifiera le client en vérifiant que son certificat est signé par une autorité acceptable. Si un certificat valide a été fourni, il peut être obtenu via l'API servlet dans une application. Le module Spring Security X.509 extrait le certificat à l'aide d'un filtre. Il mappe le certificat à un utilisateur d'application et charge l'ensemble des autorisations accordées à cet utilisateur pour l'utiliser avec l'infrastructure Spring Security standard.
et
clientAuth
peut également être défini surwant
si vous souhaitez toujours que les connexions SSL réussissent, même si le client ne fournit pas de certificat. Les clients qui ne présentent pas de certificat ne pourront accéder à aucun objet sécurisé par Spring Security à moins que vous n'utilisiez un mécanisme d'authentification non X.509, tel que l'authentification par formulaire.
J'ai rencontré un problème similaire et j'ai pensé partager la solution avec laquelle je suis venu.
Tout d'abord, vous devez comprendre que l'authentification du certificat SSL sera gérée du côté de votre serveur Web (explication du cfr. Dur, avec le paramètre "clientAuth = want"). Ensuite, votre application Web doit être configurée afin de gérer le certificat fourni (et autorisé), de le mapper à un utilisateur, etc.
La légère différence que j'ai avec vous est que j'empaquette mon application Spring Boot dans une archive WAR, qui est ensuite déployée sur un serveur d'applications Tomcat existant.
Le fichier de configuration server.xml de My Tomcat définit un connecteur HTTPS comme suit:
<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” />
Petit commentaire pour éviter toute confusion: keystoreFile contient la paire certificat/clé privée utilisée pour SSL (uniquement), tandis que truststoreFile contient les certificats CA autorisés pour l'authentification SSL client (notez que vous pouvez également ajouter les certificats client directement dans ce magasin de confiance) .
Si vous utilisez un conteneur Tomcat intégré avec votre application Spring Boot, vous devriez être en mesure de configurer ces paramètres dans le fichier de propriétés de votre application, à l'aide des valeurs/clés de propriété suivantes:
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
Ensuite, sur mon application web, je déclare une configuration SSL spécifique comme suit:
@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's properties.
auth
.inMemoryAuthentication()
.withUser(ALLOWED_USER).password("").roles("SSL_USER");
}
}
Je dois ensuite déclarer le bean UserDetailsService (par exemple dans la classe principale de mon application):
@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;
}
};
}
Et c'est tout! Je peux ensuite ajouter des annotations @PreAuthorize ("hasRole (" ROLE_SSL_USER ")") aux méthodes que je souhaite sécuriser.
Pour résumer un peu, le flux d'authentification sera le suivant: