Skip to main content

Spring Security

Framework complet pour gérer la sécurité de votre application :

  • Authentification / gestion des utilisateurs
  • Sécurisation de l'accès aux objets
  • Protection anti CSRF/XSS
  • Ajout de headers

Pour activer Spring Security, il suffit d'ajouter la dépendance dans maven :

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

Par défaut, toutes vos pages seront protégées par un formulaire de login.

La configuration de Spring Security se fait au travers d'une classe Java dans votre projet qui doit hériter de la classe WebSecurityConfigurerAdapter.

Dans cette classe, en surchargeant la méthode protected void configure(HttpSecurity http) vous pourrez préciser quelles sont les pages protégées, quelles sont les pages publiques, comment l'utilisateur doit s'authentifier, etc.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// code de configuration
}
}

Pour autoriser ou interdire des URL

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/home").permitAll() // pages autorisées
.anyRequest().authenticated() // toutes les autres sont protégées
.and()
.formLogin() // login via un formulaire
.and()
.logout();// autoriser le logout sur /logout (en POST)
}

Pour gérer des utilisateurs et des rôles, il faut retourner un UserDetailsService avec la configuration que vous souhaitez. Pour gérer le hash des mots de passe, il faut également déclarer un Bean implémentant l'interface PasswordEncoder.

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails user =
User.withUsername("user")
.password("$2y$10$OJ/J.leqEn/131hPMxhH3uPkoHOA08n2KgOYyr5oY/fpc7Ep./H6G")
.roles("USER")
.build();

return new InMemoryUserDetailsManager(user);
}

Pour les gérer avec une base de données (tables/colonnes à créer de votre côté avec Flyway) :

@Bean
@Override
public UserDetailsService userDetailsService() {
return new JdbcUserDetailsManager(dataSource);
}

Il faut créer une table users et authorities dans la base, avec les colonnes suivantes :

CREATE TABLE users(
username TEXT NOT NULL,
password TEXT NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
PRIMARY KEY (username));

CREATE TABLE authorities(
username TEXT NOT NULL,
authority TEXT NOT NULL,
FOREIGN KEY (username) REFERENCES users (username));

Pour protéger des méthodes à partir du rôle (authority) de l'utilisateur, il faut activer une configuration Spring :

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class MethodSecurityConfig
extends GlobalMethodSecurityConfiguration {
}

Vous pouvez ensuite protéger des méthodes (publiques) avec @Secured ou @PreAuthorize :

@Secured("ADMIN")
public void methode() {

}

@PreAuthorize("hasAuthority('ADMIN') or hasAuthority('USER')")
public void autreMethode() {

}

Pour gérer plus finalement les accès aux objets, vous pouvez implémenter l'interface PermissionEvaluator et l'utiliser depuis vos controllers :

@Component
public class MyPermissionEvaluator implements PermissionEvaluator {

private Logger logger = LoggerFactory.getLogger(MyPermissionEvaluator.class);

@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
return false;
}

@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
logger.info("Demande de permission pour accéder à l'objet de type {} avec l'id {} pour une opération de {} par l'utilisateur {}",
targetType,
targetId,
permission,
authentication.getName());
return false;
}
}

Une fois le PermissionEvaluator déclaré, vous pouvez utiliser l'annotation @PreAuthorize pour y faire appel, par exemple, pour vérifier qu'un utilisateur a le droit d'accéder à un article :

@GetMapping("/{id}")
@PreAuthorize("hasPermission(#id, 'ARTICLE', 'READ')")
public Article getArticleById(@PathVariable Integer id) {

La signature de hasPermission :

hasPermission(Object targetId, String targetType, Object permission)

Pour récupérer un argument de la méthode, on peut utiliser le préfixe # comme ici pour récupérer l'ID de l'article.

On peut ensuite faire toutes les vérifications nécessaires dans le PermissionEvaluator et renvoyer true si l'utilisateur a le droit, ou false pour lui refuser le droit.

Si vous avez besoin de récupérer des informations sur l'utilisateur dans votre code Java, plusieurs solutions.

Dans un controller, vous pouvez récupérer l'authentification directement via un paramètre de méthode :

@GetMapping
public void methodeDeController(Authentication authentication) {
// login de l'utilisateur
authentication.getName();
// rôles de l'utilisateur
authentication.getAuthorities();
}

N'importe où dans le code :

// retourne un objet Authentication
SecurityContextHolder.getContext().getAuthentication();