Comprendre OAuth 2.0
OAuth 2.0 est un protocole standard d'autorisation qui définit des flux spécifiques pour les applications web, mobiles et desktop. Il simplifie les processus d'accès délégué aux ressources protégées.
Le flux Authorization Code
Ce mode offre le processus d'autorisation le plus complet et sécurisé. L'interaction s'effectue principalement entre le serveur client et le serveur d'autorisation via les étapes :
- L'utilisateur est redirigé vers le serveur d'autorisation
- Validtaion des permissions par l'utilisateur
- Réception d'un code d'autorisation via l'URI de redirection
- Échange du code contre un jeton d'accès (backend)
- Founriture des jetons d'accès et de rafraîchissement
Implémentation Spring Boot
Dépendances Maven :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
Configuration de sécurité
@Configuration
public class SecuriteConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authManager() throws Exception {
return super.authenticationManagerBean();
}
@Autowired
@Qualifier("serviceUtilisateurs")
private UserDetailsService serviceUtilisateurs;
@Override
protected void configurerAuth(AuthenticationManagerBuilder auth) {
auth.userDetailsService(serviceUtilisateurs);
}
@Override
protected void configurerHttp(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**/*.js", "/**/*.css").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginPage("/connexion").permitAll()
.and().csrf().disable();
}
}
Serveur d'autorisation OAuth
@Configuration
@EnableAuthorizationServer
public class ConfigOAuth extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager gestionAuth;
@Autowired
@Qualifier("serviceUtilisateurs")
private UserDetailsService serviceUtilisateurs;
@Override
public void configurerSecurite(AuthorizationServerSecurityConfigurer sec) {
sec.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
@Override
public void configurerClients(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("monClient")
.secret("{noop}secretClient")
.scopes("complet")
.authorizedGrantTypes("authorization_code")
.autoApprove(true);
}
@Override
public void configurerPointsFinaux(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(gestionAuth)
.tokenStore(stockageMemoire());
}
@Bean
public TokenStore stockageMemoire() {
return new InMemoryTokenStore();
}
}
Fournisseur d'authentification personnalisé
public class AuthProviderCustom implements AuthenticationProvider {
private final UserDetailsService serviceUtilisateurs;
private final BCryptPasswordEncoder encodeur;
public AuthProviderCustom(UserDetailsService s, BCryptPasswordEncoder e) {
this.serviceUtilisateurs = s;
this.encodeur = e;
}
@Override
public Authentication authentifier(Authentication auth) throws AuthenticationException {
String identifiant = auth.getName();
String motdepasse = auth.getCredentials().toString();
UserDetails utilisateur = serviceUtilisateurs.loadUserByUsername(identifiant);
if (utilisateur != null) {
if (encodeur.matches(motdepasse, utilisateur.getPassword())) {
List<GrantedAuthority> droits = Arrays.asList(
new SimpleGrantedAuthority("ROLE_ADMIN"),
new SimpleGrantedAuthority("AUTH_ECRITURE")
);
return new UsernamePasswordAuthenticationToken(identifiant, motdepasse, droits);
}
throw new BadCredentialsException("Mot de passe invalide");
}
throw new UsernameNotFoundException("Utilisateur introuvable");
}
@Override
public boolean supports(Class<?> typeAuth) {
return typeAuth.equals(UsernamePasswordAuthenticationToken.class);
}
}