0Pricing
Spring Boot 4 Complete Guide · Pelajaran

Keamanan Metode dengan SpEL dan Pemilih Kustom

Terapkan kontrol akses terperinci menggunakan @PreAuthorize, ekspresi SpEL, dan logika otorisasi khusus.

Keamanan Metode dengan SpEL dan Pemilih Kustom adalah pelajaran Spring Boot 4 Complete Guide gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Spring Boot 4 Complete Guide, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Spring Boot 4 Complete Guide mencakup 4 pelajaran total.

Bagian dari pelajaran ini belum diterjemahkan dan ditampilkan dalam bahasa Inggris.

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.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Keamanan Metode dengan SpEL dan Pemilih Kustom” gratis?

Ya — teks lengkap “Keamanan Metode dengan SpEL dan Pemilih Kustom” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Spring Boot 4 Complete Guide, upgrade ke CoddyKit PRO. Kursus Spring Boot 4 Complete Guide mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Keamanan Metode dengan SpEL dan Pemilih Kustom”?

Terapkan kontrol akses terperinci menggunakan @PreAuthorize, ekspresi SpEL, dan logika otorisasi khusus. Kamu berlatih Spring Boot 4 Complete Guide dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai Spring Boot 4 Complete Guide?

Tidak diperlukan pengalaman sebelumnya. Spring Boot 4 Complete Guide di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.

Berapa lama pelajaran “Keamanan Metode dengan SpEL dan Pemilih Kustom” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran Spring Boot 4 Complete Guide ini?

Ya. Setiap pelajaran Spring Boot 4 Complete Guide menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Validasi JWT dan Klaim Server Sumber Daya
  2. Klien OAuth2 dan Alur Kode Otorisasi
  3. Keamanan Metode dengan SpEL dan Pemilih Kustom
  4. Introspeksi Token Opaque dan Pertukaran Token
← Kembali ke Spring Boot 4 Complete Guide