Spring Boot 4 Complete Guide · Leçon

Sécurité des méthodes avec SpEL et votants personnalisés

Appliquez un contrôle d’accès granulaire à l’aide de @PreAuthorize, des expressions SpEL et d’une logique d’autorisation personnalisée.

Leçon 3 sur 413 étapes

Sécurité des méthodes avec SpEL et votants personnalisés est une leçon Spring Boot 4 Complete Guide gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Spring Boot 4 Complete Guide, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Spring Boot 4 Complete Guide comprend 4 leçons au total.

Certaines parties de cette leçon n'ont pas encore été traduites et s'affichent en anglais.

Why Method Security?

URL-based security (HttpSecurity matchers) guards entry points, but it cannot see the arguments a method receives or the object it returns. Method security closes that gap by enforcing rules right at the service layer.

  • Defense in depth — protection survives even if a controller forgets a check.
  • Fine-grained — decide based on parameters, return values, and the authenticated principal.
  • Reusable — the same secured service can be called from REST, GraphQL, or a message listener and stays protected.

In this lesson we enforce access with @PreAuthorize, SpEL expressions, and a custom authorization manager.

Enabling Method Security

In Spring Boot 4 / Spring Security 6, method security is opt-in. Add @EnableMethodSecurity to a configuration class. It activates the annotations through an AOP proxy.

  • prePostEnabled defaults to true — @PreAuthorize and @PostAuthorize work out of the box.
  • Set securedEnabled = true for the legacy @Secured, or jsr250Enabled = true for @RolesAllowed.

Note: the old @EnableGlobalMethodSecurity is removed — always use @EnableMethodSecurity.

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

@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig {
    // prePostEnabled = true by default
    // @PreAuthorize / @PostAuthorize now active
}

@PreAuthorize with Roles and Authorities

@PreAuthorize evaluates a SpEL expression before the method runs. If it returns false, Spring throws AccessDeniedException and the body never executes.

  • hasRole('ADMIN') — checks the ROLE_ADMIN authority (the prefix is added for you).
  • hasAuthority('SCOPE_orders:write') — exact authority match, no prefix added.
  • hasAnyRole('ADMIN','MANAGER') and the boolean operators and / or / !.
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
public class AccountService {

    @PreAuthorize("hasRole('ADMIN')")
    public void closeAccount(Long accountId) {
        // only ROLE_ADMIN reaches here
    }

    @PreAuthorize("hasAnyRole('ADMIN','SUPPORT') or hasAuthority('SCOPE_accounts:write')")
    public void freezeAccount(Long accountId) {
        // ...
    }
}

Referencing Method Arguments with #

The real power of SpEL is reading method arguments. Prefix a parameter name with # to use it inside the expression. This lets you compare the principal's identity to the data being acted on.

  • authentication — the current Authentication object.
  • principal — the principal (often a UserDetails or JWT).
  • #username, #order.ownerId — method arguments and their properties.

Argument names require parameters compiled with -parameters (Spring Boot enables this by default).

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
public class ProfileService {

    // A user may edit only their own profile, unless they are an admin
    @PreAuthorize("#username == authentication.name or hasRole('ADMIN')")
    public void updateProfile(String username, ProfileDto dto) {
        // ...
    }
}

@PostAuthorize and returnObject

@PostAuthorize runs after the method returns and can inspect the result via returnObject. Use it when you must load the entity first to know who owns it.

  • Good for “you can read this record only if it belongs to you.”
  • The method body does execute, so avoid it for operations with side effects you must prevent.
  • On denial, the return value is discarded and AccessDeniedException is thrown.
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.stereotype.Service;

@Service
public class DocumentService {

    @PostAuthorize("returnObject.ownerUsername == authentication.name or hasRole('ADMIN')")
    public Document findById(Long id) {
        return repository.findById(id).orElseThrow();
    }
}

@PreFilter and @PostFilter on Collections

Filtering annotations prune collections element by element instead of throwing. They use a special variable filterObject bound to each element.

  • @PreFilter — strips disallowed elements from a collection argument before the method runs.
  • @PostFilter — strips disallowed elements from the returned collection.
  • Use filterTarget when a method has more than one collection parameter.

Caution: post-filtering large result sets in memory can be costly — prefer filtering in the query when possible.

import org.springframework.security.access.prepost.PostFilter;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class OrderService {

    // Caller sees only the orders they own (admins see all)
    @PostFilter("filterObject.ownerUsername == authentication.name or hasRole('ADMIN')")
    public List<Order> findRecentOrders() {
        return repository.findRecent();
    }
}

Calling a Bean from SpEL with @

When logic gets complex, push it into a Spring bean and call it from the expression using @beanName.method(...). This keeps annotations readable and the rule unit-testable.

  • The @ resolves a bean from the application context.
  • Pass authentication and method arguments straight into the bean method.
  • The method must return a boolean.
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

@Component("projectAccess")
public class ProjectAccessEvaluator {

    public boolean canEdit(Authentication auth, Long projectId) {
        String user = auth.getName();
        return membershipRepository.isEditor(user, projectId);
    }
}

// Usage on a service method:
// @PreAuthorize("@projectAccess.canEdit(authentication, #projectId)")
// public void rename(Long projectId, String name) { ... }

PermissionEvaluator and hasPermission

SpEL exposes hasPermission(target, permission), which delegates to a PermissionEvaluator bean. It is the canonical hook for domain-object (ACL-style) authorization without inlining logic in every annotation.

  • hasPermission(#doc, 'WRITE') — passes the object and a permission key.
  • hasPermission(#id, 'com.app.Document', 'READ') — passes an id plus the type.
  • You register exactly one PermissionEvaluator via a MethodSecurityExpressionHandler.
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import java.io.Serializable;

public class DocumentPermissionEvaluator implements PermissionEvaluator {

    @Override
    public boolean hasPermission(Authentication auth, Object target, Object permission) {
        if (target instanceof Document doc) {
            return "WRITE".equals(permission)
                ? doc.getOwnerUsername().equals(auth.getName())
                : true; // READ allowed for all in this example
        }
        return false;
    }

    @Override
    public boolean hasPermission(Authentication auth, Serializable id,
                                 String type, Object permission) {
        return false; // resolve by id+type if needed
    }
}

Registering a Custom Expression Handler

To wire your PermissionEvaluator into SpEL, expose a DefaultMethodSecurityExpressionHandler bean and set the evaluator on it. Spring Security picks it up for all method annotations.

  • The bean name is not important; the type is.
  • You can also attach a custom RoleHierarchy here so hasRole respects inheritance.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;

@Configuration
@EnableMethodSecurity
public class ExpressionHandlerConfig {

    @Bean
    static DefaultMethodSecurityExpressionHandler expressionHandler() {
        var handler = new DefaultMethodSecurityExpressionHandler();
        handler.setPermissionEvaluator(new DocumentPermissionEvaluator());
        return handler;
    }
}

Custom AuthorizationManager (the New Voter)

Spring Security 6 replaced the legacy AccessDecisionVoter with the simpler AuthorizationManager<T>. For method security the type parameter is MethodInvocation. Implement check to return an AuthorizationDecision.

  • Return new AuthorizationDecision(true|false) — or null to abstain and let other managers decide.
  • Register it with @EnableMethodSecurity(prePostEnabled = false) plus an advisor, or combine managers with AuthorizationManagers.allOf(...).
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import java.util.function.Supplier;

public class BusinessHoursAuthorizationManager
        implements AuthorizationManager<MethodInvocation> {

    @Override
    public AuthorizationDecision check(Supplier<Authentication> auth,
                                       MethodInvocation invocation) {
        int hour = java.time.LocalTime.now().getHour();
        boolean withinHours = hour >= 9 && hour < 18;
        return new AuthorizationDecision(withinHours);
    }
}

Pre vs Post: Choosing Correctly

Picking the wrong annotation either leaks data or blocks valid calls. A quick decision guide:

  • Rule depends only on arguments + principal → @PreAuthorize (fast, no side effects).
  • Rule depends on the loaded entity's ownership → @PostAuthorize.
  • Trimming a collection per-element → @PostFilter (or filter in the query).
  • Reusable domain-object rule → hasPermission + PermissionEvaluator.

Remember: @PostAuthorize and @PostFilter run the method body, so never rely on them to stop a mutating operation.

Quick Check

You have a method Document findById(Long id) that loads a document, and access should be granted only if the returned document's ownerUsername equals the caller, or the caller is an admin. Which annotation expresses this correctly?

Recap

You now enforce fine-grained access at the method layer:

  • @EnableMethodSecurity turns on annotation-driven checks (no more @EnableGlobalMethodSecurity).
  • @PreAuthorize guards before execution using SpEL: hasRole, hasAuthority, #args, and authentication.
  • @PostAuthorize inspects returnObject; @PreFilter/@PostFilter prune collections via filterObject.
  • Push complex rules into a bean (@beanName.method(...)) or a PermissionEvaluator behind hasPermission.
  • For cross-cutting policy, implement AuthorizationManager<MethodInvocation> — the modern replacement for voters.

Rule of thumb: prefer pre-checks for speed and safety; reach for post-checks only when the decision needs the loaded data.

Gratuit pour commencer

Apprends Java avec un tuteur IA — gratuit

Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.

Cours
21
Leçons
84

Questions Fréquemment Posées

La leçon « Sécurité des méthodes avec SpEL et votants personnalisés » est-elle gratuite ?

Oui — le texte complet de « Sécurité des méthodes avec SpEL et votants personnalisés » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Spring Boot 4 Complete Guide, passe à CoddyKit PRO. Le cours Spring Boot 4 Complete Guide comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Sécurité des méthodes avec SpEL et votants personnalisés » ?

Appliquez un contrôle d’accès granulaire à l’aide de @PreAuthorize, des expressions SpEL et d’une logique d’autorisation personnalisée. Tu pratiques Spring Boot 4 Complete Guide avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Spring Boot 4 Complete Guide ?

Aucune expérience préalable n'est requise. Spring Boot 4 Complete Guide sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Sécurité des méthodes avec SpEL et votants personnalisés » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Spring Boot 4 Complete Guide ?

Oui. Chaque leçon Spring Boot 4 Complete Guide inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Validation des JWT du serveur de ressources et revendications
  2. Client OAuth2 et flux du code d’autorisation
  3. Sécurité des méthodes avec SpEL et votants personnalisés
  4. Introspection des jetons opaques et échange de jetons
← Retour à Spring Boot 4 Complete Guide