ReviseAlgo Logo

Spring Security

Method-Level Security — @PreAuthorize, @PostAuthorize

Enforcing role clearances and domain permissions check hooks directly on Java methods.

Last Updated: June 14, 2026 12 min read

Introduction

Spring Security allows enforcing authorization constraints directly on Java methods. Rather than checking clearances solely at URL request paths, you can declare security constraints at the service or data layers using annotations.

Why It Matters

Url-based authorization (e.g., requestMatchers("/admin/**").hasRole("ADMIN")) is clean but susceptible to route bypasses. If developers expose a new route pattern (like /internal/admin/config) that escapes path filter matchers, the API is exposed. Method security acts as a robust secondary security boundary, blocking access regardless of how the method is invoked.

Real-World Analogy

Think of method-level security like bank vault clearance checks. Checking credentials at the entrance doors (URL request filters) lets you enter the lobby. However, opening a safe deposit box (executing a service method) requires matching owner verification, irrespective of how you got inside the lobby.

Detailed Mechanics

1. Enabling Method Security

Method security is disabled by default. To enable it, you must annotate a configuration bean with @EnableMethodSecurity. Spring then uses AOP proxies to intercept annotated methods at runtime.

2. PreAuthorize vs PostAuthorize

  • @PreAuthorize: Evaluates a SpEL expression *before* entering the method. If the expression evaluates to false, Spring immediately blocks execution and throws an AccessDeniedException. Useful for role checks or checking input arguments.
  • @PostAuthorize: Evaluates a SpEL expression *after* the method finishes execution. The return object is accessible using the special variable returnObject. If the expression is false, Spring throws AccessDeniedException, preventing the caller from seeing the returned data. Useful for checking resource ownership.

3. SpEL Context Integration

Method annotations support **Spring Expression Language (SpEL)**. You can reference the principal user details (e.g. principal.username), client roles, or check method arguments directly by using parameter names prefixed with a hash (e.g. #order).

Practical Example

Below is a service class illustrating method-level security for role checks and owner constraints:

Quick Quiz

Q1: What is the main difference in behavior between @PreAuthorize and @PostAuthorize?

A) @PreAuthorize is only for administrators, while @PostAuthorize is for normal users.

B) @PreAuthorize runs before executing the method; @PostAuthorize executes the method first, then validates permissions on the returned object before returning it.

C) @PostAuthorize updates database values automatically.

D) @PreAuthorize does not support SpEL expressions.

Answer: B — @PostAuthorize lets you fetch data first, and then evaluate rules on the resulting object using returnObject properties.

Q2: How do you access a method parameter inside a @PreAuthorize SpEL expression?

A) Using the paramPrefix keyword

B) By referencing the argument name directly without any prefix

C) Using a hash character prefix (#) followed by the parameter name

D) Parameter referencing is not supported in method security

Answer: C — SpEL references method parameters using the hash symbol prefix (e.g. #accountId).

Scenario-Based Challenge

Production Scenario:

A developer configures method-level security on a service class. However, they complain that when a non-authenticated user calls the method, they receive a NullPointerException instead of the expected AccessDeniedException. What is the most likely cause?

View Solution

This happens when the SpEL expression attempts to read properties from an un-authenticated principal (e.g., evaluating principal.username when principal is null). To prevent this:

  1. Use safety check operators: principal?.username or check if the user is authenticated first: isAuthenticated() and #owner == principal.username.
  2. Ensure the security context filter chain runs before the service invocation so that standard authentication validation triggers first.

Interview Questions

1. Conceptual: Can method-level security be applied to private methods?

No. Spring Security relies on AOP proxies to intercept calls. AOP proxies can only intercept public method invocations. Annotating private or protected methods with method security annotations has no effect.

2. Concept: What is the difference between @PreAuthorize and @Secured?

@Secured is a legacy annotation that only supports simple role string mappings (e.g., @Secured("ROLE_ADMIN")). @PreAuthorize is modern and supports complete SpEL expression evaluations, allowing advanced conditional queries and parameter checks.

Production Considerations

Avoid writing overly complex logic directly inside SpEL strings in Java annotations. Complex logic is hard to test and maintain. Instead, write a dedicated authorization bean (e.g. @PreAuthorize("@mySecurityBean.hasAccess(#docId)")) and delegate the evaluation to standard, unit-testable Java code.