Contactez-nous

Sécurité au niveau des méthodes avec `@PreAuthorize`, `@PostAuthorize`, `@Secured`, `@RolesAllowed`

Apprenez à appliquer un contrôle d'accès fin au niveau des méthodes dans Spring Security en utilisant les annotations @PreAuthorize, @PostAuthorize, @Secured et @RolesAllowed.

Un contrôle d'accès plus fin : Au-delà des URLs

La configuration de la sécurité basée sur les URLs (via HttpSecurity et requestMatchers) est essentielle pour protéger des sections entières de votre application. Cependant, elle manque souvent de granularité. Parfois, vous avez besoin d'appliquer des règles d'autorisation différentes à des méthodes spécifiques au sein d'un même contrôleur ou service, ou de baser la décision d'accès sur les arguments de la méthode ou sur l'objet retourné.

C'est là qu'intervient la sécurité au niveau des méthodes de Spring Security. Elle permet d'annoter directement vos méthodes Java (généralement dans les couches @Service ou @Repository, mais aussi parfois dans les @Controller) pour définir des conditions d'autorisation précises. Spring Security utilise alors AOP (Programmation Orientée Aspect) pour intercepter les appels à ces méthodes et vérifier si l'utilisateur authentifié a les droits nécessaires avant (ou après) l'exécution de la méthode.

Quatre annotations principales sont utilisées pour cela : @PreAuthorize, @PostAuthorize, @Secured, et l'annotation standard JSR-250 @RolesAllowed.

Activer la sécurité au niveau méthode

Avant de pouvoir utiliser ces annotations, vous devez activer la fonctionnalité dans votre configuration Spring Security. Cela se fait en ajoutant l'annotation @EnableMethodSecurity (remplace l'ancienne @EnableGlobalMethodSecurity) à l'une de vos classes @Configuration.

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;

@Configuration
@EnableMethodSecurity( // Active les annotations de sécurité sur les méthodes
    securedEnabled = true, // Active le support pour @Secured
    jsr250Enabled = true // Active le support pour @RolesAllowed
    // prePostEnabled = true est activé par défaut avec @EnableMethodSecurity
)
public class MethodSecurityConfig {
    // ... éventuellement d'autres beans de configuration
}

Par défaut, @EnableMethodSecurity active déjà @PreAuthorize et @PostAuthorize. Les attributs securedEnabled et jsr250Enabled permettent d'activer explicitement les autres types d'annotations si vous souhaitez les utiliser.

`@PreAuthorize` : Vérification avant l'exécution

C'est l'annotation de sécurité au niveau méthode la plus puissante et la plus recommandée. Elle permet d'évaluer une expression SpEL (Spring Expression Language) avant que la méthode annotée ne soit exécutée. Si l'expression évalue à false, une exception AccessDeniedException est levée et la méthode n'est jamais appelée.

SpEL vous donne accès à :

  • Des expressions simples comme hasRole('ROLE_ADMIN'), hasAuthority('permission:write'), isAuthenticated(), isAnonymous().
  • L'objet principal (représentant l'utilisateur authentifié).
  • Les paramètres de la méthode (en utilisant #paramName).
  • D'autres beans Spring (en utilisant @beanName.method(...)).
@Service
public class DocumentService {

    @PreAuthorize("hasRole('ROLE_ADMIN')") // Seuls les ADMIN peuvent appeler cette méthode
    public void deleteAllDocuments() {
        System.out.println("Suppression de tous les documents...");
        // ... logique de suppression
    }

    @PreAuthorize("hasAuthority('document:read') or hasRole('ROLE_ADMIN')") // Besoin de la permission ou être ADMIN
    public Document findDocumentById(Long id) {
        System.out.println("Recherche du document: " + id);
        // ... logique de recherche
        return new Document(id, "Contenu secret");
    }

    // Vérifie que l'utilisateur authentifié est le propriétaire du document
    // ou qu'il est ADMIN. Accède au paramètre 'documentId' et à l'objet 'principal'.
    @PreAuthorize("hasRole('ROLE_ADMIN') or @documentRepository.findById(#documentId)?.owner == authentication.name")
    public void updateDocument(Long documentId, String content) {
        System.out.println("Mise à jour du document: " + documentId);
        // Note: L'appel @documentRepository... suppose un bean 'documentRepository'
        // L'opérateur '?.' est un 'safe navigation operator' SpEL.
        // 'authentication.name' récupère le nom de l'utilisateur authentifié.
        // ... logique de mise à jour
    }

    @PreAuthorize("#username == authentication.name") // L'utilisateur ne peut voir que son propre profil
    public UserProfile getUserProfile(String username) {
        System.out.println("Récupération du profil pour: " + username);
        // ... logique
        return new UserProfile(username);
    }
}

@PreAuthorize est très flexible car il permet d'implémenter des logiques d'autorisation complexes basées sur les rôles, les permissions, les paramètres de la méthode et le contexte d'exécution.

`@PostAuthorize` : Vérification après l'exécution

Cette annotation évalue également une expression SpEL, mais elle le fait après l'exécution de la méthode et juste avant que le résultat ne soit retourné à l'appelant. Elle est moins courante que @PreAuthorize car elle ne peut pas empêcher l'exécution de la méthode elle-même (ce qui peut avoir des effets de bord).

Son principal cas d'usage est d'appliquer des règles d'autorisation basées sur l'objet retourné par la méthode. SpEL a accès à l'objet retourné via la variable returnObject.

@Service
public class SensitiveDataService {

    // La méthode est exécutée, mais si l'utilisateur authentifié n'est pas
    // le propriétaire des données retournées, une AccessDeniedException est levée.
    @PostAuthorize("returnObject.owner == authentication.name or hasRole('ROLE_ADMIN')")
    public SensitiveData findSensitiveDataById(Long id) {
        System.out.println("Recherche des données sensibles: " + id);
        // ... logique pour récupérer les données
        SensitiveData data = // ...
        // La vérification se fait ici, avant de retourner 'data'
        return data;
    }
}

class SensitiveData {
    private String owner; // Nom du propriétaire
    private String secretContent;
    // getters...
}

Utilisez @PostAuthorize avec prudence, car la méthode aura déjà été exécutée. Elle est surtout utile pour des vérifications finales basées sur le résultat.

`@Secured` : Sécurité simple basée sur les rôles/autorités

C'est l'annotation de sécurité méthode originale de Spring. Elle est plus simple que @PreAuthorize mais moins flexible. Elle permet de spécifier une liste de rôles ou d'autorités requis pour exécuter la méthode. L'utilisateur doit posséder au moins un des rôles/autorités listés.

Elle ne supporte pas SpEL.

import org.springframework.security.access.annotation.Secured;

@Service
public class AdminService {

    // L'utilisateur doit avoir soit ROLE_ADMIN, soit ROLE_SUPER_ADMIN
    @Secured({"ROLE_ADMIN", "ROLE_SUPER_ADMIN"})
    public void performAdminTask() {
        System.out.println("Exécution tâche admin...");
        // ...
    }

    // L'utilisateur doit avoir l'autorité 'OP_SYSTEM_SHUTDOWN'
    @Secured("OP_SYSTEM_SHUTDOWN")
    public void shutdownSystem() {
        System.out.println("Arrêt système...");
        // ...
    }
}

Par défaut, les chaînes dans @Secured sont interprétées comme des autorités. Si vous voulez spécifiquement vérifier des rôles (qui sont souvent préfixés par `ROLE_`), assurez-vous que vos GrantedAuthority retournées par le UserDetailsService incluent ce préfixe, ou configurez Spring Security pour qu'il ajoute le préfixe `ROLE_` automatiquement lors de l'évaluation de @Secured (ce qui n'est pas le comportement par défaut le plus récent).

@Secured est utile pour des règles simples basées sur une liste fixe de rôles/autorités.

`@RolesAllowed` (JSR-250) : Standard Java pour les rôles

Cette annotation fait partie du standard Java (JSR-250). Elle est très similaire à @Secured mais est spécifiquement conçue pour vérifier les rôles. L'utilisateur doit posséder au moins un des rôles listés.

Elle ne supporte pas SpEL et n'est destinée qu'aux rôles (le préfixe `ROLE_` est généralement attendu par défaut, bien que cela puisse être configuré).

import javax.annotation.security.RolesAllowed;

@Service
public class ManagerService {

    // L'utilisateur doit avoir le rôle "MANAGER" ou "ADMIN"
    // Spring Security ajoutera typiquement le préfixe "ROLE_" lors de la vérification
    @RolesAllowed({"MANAGER", "ADMIN"})
    public void approveExpense(Long expenseId) {
        System.out.println("Approbation dépense: " + expenseId);
        // ...
    }
}

@RolesAllowed est une alternative standard à @Secured si vous ne travaillez qu'avec des rôles et préférez utiliser les annotations JSR-250.

Choisir la bonne annotation

  • @PreAuthorize : Le choix le plus puissant et flexible grâce à SpEL. Idéal pour la majorité des cas, y compris les règles complexes basées sur les paramètres, les rôles, les permissions ou des logiques métier externes.
  • @PostAuthorize : A utiliser lorsque la décision d'autorisation dépend du résultat de la méthode. A utiliser avec précaution.
  • @Secured : Pour des vérifications simples basées sur une liste d'autorités ou de rôles (OU logique).
  • @RolesAllowed : Alternative standard JSR-250 à @Secured, spécifiquement pour les rôles.

En général, privilégiez @PreAuthorize pour sa flexibilité et sa puissance expressive grâce à SpEL. Utilisez les autres annotations pour des cas plus simples ou si vous avez des contraintes spécifiques (standard JSR-250, compatibilité avec du code existant).

La sécurité au niveau méthode offre un contrôle d'accès fin et puissant, complétant efficacement la sécurité basée sur les URLs pour protéger précisément les fonctionnalités et les données de votre application Spring Boot.