or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication.mdindex.mdkotlin-dsl.mdmethod-security.mdoauth2.mdreactive-web-security.mdsaml2.mdservlet-web-security.md
tile.json

method-security.mddocs/

Method Security

Method-level authorization using annotations on service methods with support for SpEL expressions for complex authorization logic.

Capabilities

Enabling Method Security

Enable method-level security using @EnableMethodSecurity.

package org.springframework.security.config.annotation.method.configuration;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({PrePostMethodSecurityConfiguration.class})
public @interface EnableMethodSecurity {
    /**
     * Enable @PreAuthorize/@PostAuthorize annotations (default: true)
     */
    boolean prePostEnabled() default true;

    /**
     * Enable @Secured annotation (default: false)
     */
    boolean securedEnabled() default false;

    /**
     * Enable JSR-250 annotations (@RolesAllowed, @PermitAll, @DenyAll) (default: false)
     */
    boolean jsr250Enabled() default false;

    /**
     * Use CGLIB proxies instead of JDK dynamic proxies (default: false)
     */
    boolean proxyTargetClass() default false;

    /**
     * Advice mode (PROXY or ASPECTJ) (default: PROXY)
     */
    AdviceMode mode() default AdviceMode.PROXY;

    /**
     * Authorization check offset (default: 0)
     */
    int offset() default 0;
}

Usage:

@Configuration
@EnableMethodSecurity(
    prePostEnabled = true,
    securedEnabled = false,
    jsr250Enabled = false
)
public class MethodSecurityConfig {
}

Migration Note: @EnableGlobalMethodSecurity is deprecated as of Spring Security 5.6. Use @EnableMethodSecurity instead, which provides the same functionality with better defaults and improved performance.

// Deprecated (Spring Security 5.5 and earlier)
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)

// Current (Spring Security 5.6+)
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)

Enabling Reactive Method Security

Enable method-level security for reactive (WebFlux) applications.

package org.springframework.security.config.annotation.method.configuration;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ReactiveMethodSecuritySelector.class)
public @interface EnableReactiveMethodSecurity {
    /**
     * Use CGLIB proxies instead of JDK dynamic proxies (default: false)
     */
    boolean proxyTargetClass() default false;

    /**
     * Advice mode (PROXY or ASPECTJ) (default: PROXY)
     */
    AdviceMode mode() default AdviceMode.PROXY;

    /**
     * Ordering for security advisor (default: LOWEST_PRECEDENCE)
     */
    int order() default Ordered.LOWEST_PRECEDENCE;

    /**
     * Use ReactiveAuthorizationManager-based Method Security (default: true)
     * @since 5.8
     */
    boolean useAuthorizationManager() default true;
}

Usage:

@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class ReactiveMethodSecurityConfig {
}

// Use the same annotations on reactive methods
@Service
public class ReactiveSecureService {

    @PreAuthorize("hasRole('ADMIN')")
    public Mono<String> adminOnlyMethod() {
        return Mono.just("admin data");
    }

    @PreAuthorize("hasAuthority('READ_PRIVILEGE')")
    public Flux<String> readData() {
        return Flux.just("data1", "data2", "data3");
    }

    @PostAuthorize("returnObject.owner == authentication.name")
    public Mono<Document> getDocument(Long id) {
        return documentRepository.findById(id);
    }
}

@PreAuthorize Annotation

Check authorization before method execution.

package org.springframework.security.access.prepost;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreAuthorize {
    /**
     * SpEL expression for authorization check
     */
    String value();
}

Usage Examples:

@Service
public class SecureService {

    // Role-based access
    @PreAuthorize("hasRole('ADMIN')")
    public void adminOnlyMethod() {
        // Only accessible by users with ADMIN role
    }

    // Multiple roles
    @PreAuthorize("hasAnyRole('USER', 'ADMIN')")
    public void userOrAdminMethod() {
        // Accessible by USER or ADMIN
    }

    // Authority-based access
    @PreAuthorize("hasAuthority('READ_PRIVILEGE')")
    public String readData() {
        return "sensitive data";
    }

    // Multiple authorities
    @PreAuthorize("hasAnyAuthority('READ', 'WRITE', 'ADMIN')")
    public void multipleAuthorities() {
    }

    // Method parameter access
    @PreAuthorize("#username == authentication.name")
    public void updateUser(String username, User user) {
        // Only the user themselves can update
    }

    // Object property access
    @PreAuthorize("#user.id == authentication.principal.id")
    public void updateUserDetails(User user) {
        // Check user ID matches authenticated user
    }

    // Complex expressions
    @PreAuthorize("hasRole('ADMIN') or #id == authentication.principal.id")
    public User getUser(Long id) {
        // Admin can access any user, others only themselves
    }

    // Authentication check
    @PreAuthorize("isAuthenticated()")
    public void authenticatedOnlyMethod() {
    }

    // Anonymous check
    @PreAuthorize("isAnonymous()")
    public void anonymousOnlyMethod() {
    }

    // Fully authenticated (not remember-me)
    @PreAuthorize("isFullyAuthenticated()")
    public void fullyAuthenticatedMethod() {
    }

    // IP address check
    @PreAuthorize("hasIpAddress('192.168.1.0/24')")
    public void internalNetworkMethod() {
    }

    // Custom security expression
    @PreAuthorize("@customSecurityService.check(authentication, #id)")
    public void customSecurityCheck(Long id) {
    }
}

// Custom security service
@Service("customSecurityService")
public class CustomSecurityService {
    public boolean check(Authentication authentication, Long id) {
        // Custom authorization logic
        return true;
    }
}

@PostAuthorize Annotation

Check authorization after method execution, allowing access to return value.

package org.springframework.security.access.prepost;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PostAuthorize {
    /**
     * SpEL expression for authorization check
     */
    String value();
}

Usage Examples:

@Service
public class DocumentService {

    // Check return value property
    @PostAuthorize("returnObject.owner == authentication.name")
    public Document getDocument(Long id) {
        return documentRepository.findById(id);
    }

    // Access return value attributes
    @PostAuthorize("returnObject != null and returnObject.public == true or hasRole('ADMIN')")
    public Post getPost(Long id) {
        return postRepository.findById(id);
    }

    // Complex return value checks
    @PostAuthorize("@documentPermissionEvaluator.canRead(authentication, returnObject)")
    public Document getSecureDocument(Long id) {
        return documentRepository.findById(id);
    }
}

@PreFilter Annotation

Filter collection/array method parameters before execution.

package org.springframework.security.access.prepost;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreFilter {
    /**
     * SpEL expression for filtering
     */
    String value();

    /**
     * Parameter name to filter (required if multiple collection parameters)
     */
    String filterTarget() default "";
}

Usage Examples:

@Service
public class DataService {

    // Filter list before processing
    @PreFilter("filterObject.owner == authentication.name")
    public void updateDocuments(List<Document> documents) {
        // Only documents owned by current user will be processed
        documentRepository.saveAll(documents);
    }

    // Filter array
    @PreFilter("filterObject != null and filterObject.active == true")
    public void processItems(Item[] items) {
        // Only active items will be processed
    }

    // Specify filter target when multiple collections
    @PreFilter(value = "filterObject.approved == true", filterTarget = "items")
    public void processMultiple(List<Item> items, Set<Category> categories) {
        // Only items collection will be filtered
    }
}

@PostFilter Annotation

Filter collection/array return values after execution.

package org.springframework.security.access.prepost;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PostFilter {
    /**
     * SpEL expression for filtering
     */
    String value();
}

Usage Examples:

@Service
public class DocumentService {

    // Filter returned list
    @PostFilter("filterObject.owner == authentication.name or hasRole('ADMIN')")
    public List<Document> getAllDocuments() {
        // Return only documents owned by user (unless ADMIN)
        return documentRepository.findAll();
    }

    // Filter by permission
    @PostFilter("@documentPermissionEvaluator.canRead(authentication, filterObject)")
    public List<Document> getAccessibleDocuments() {
        return documentRepository.findAll();
    }

    // Complex filtering
    @PostFilter("filterObject.public == true or filterObject.sharedWith.contains(authentication.name)")
    public List<Post> getPosts() {
        return postRepository.findAll();
    }
}

@Secured Annotation

Role-based authorization using @Secured (requires securedEnabled = true).

package org.springframework.security.access.annotation;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Secured {
    /**
     * List of roles (with ROLE_ prefix)
     */
    String[] value();
}

Usage Examples:

@Service
public class AdminService {

    @Secured("ROLE_ADMIN")
    public void adminOnlyMethod() {
    }

    @Secured({"ROLE_USER", "ROLE_ADMIN"})
    public void userOrAdmin() {
    }
}

JSR-250 Annotations

Standard Java security annotations (requires jsr250Enabled = true).

package jakarta.annotation.security;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RolesAllowed {
    String[] value();
}

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermitAll {
}

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DenyAll {
}

Usage Examples:

@Service
public class JsrSecureService {

    @RolesAllowed("ADMIN")
    public void adminOnly() {
    }

    @RolesAllowed({"USER", "ADMIN"})
    public void userOrAdmin() {
    }

    @PermitAll
    public void publicMethod() {
    }

    @DenyAll
    public void neverAccessible() {
    }
}

Reactive Method Security

Enable method security for reactive applications.

package org.springframework.security.config.annotation.method.configuration;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ReactiveAuthorizationManagerMethodSecurityConfiguration.class})
public @interface EnableReactiveMethodSecurity {
    boolean proxyTargetClass() default false;
    AdviceMode mode() default AdviceMode.PROXY;
    int order() default Ordered.LOWEST_PRECEDENCE;
    boolean useAuthorizationManager() default true;
}

Usage:

@Configuration
@EnableReactiveMethodSecurity
public class ReactiveMethodSecurityConfig {
}

@Service
public class ReactiveSecureService {

    @PreAuthorize("hasRole('ADMIN')")
    public Mono<String> getSecretData() {
        return Mono.just("secret");
    }

    @PostAuthorize("returnObject.owner == authentication.name")
    public Mono<Document> getDocument(Long id) {
        return documentRepository.findById(id);
    }

    @PostFilter("filterObject.public == true or filterObject.owner == authentication.name")
    public Flux<Post> getPosts() {
        return postRepository.findAll();
    }
}

SpEL Security Expressions

Common SpEL expressions for method security:

ExpressionDescription
hasRole('ROLE')True if user has specified role (without ROLE_ prefix)
hasAnyRole('ROLE1', 'ROLE2')True if user has any of the specified roles
hasAuthority('AUTHORITY')True if user has specified authority
hasAnyAuthority('AUTH1', 'AUTH2')True if user has any of the specified authorities
principalCurrent user principal object
authenticationCurrent Authentication object
isAuthenticated()True if user is authenticated
isAnonymous()True if user is anonymous
isFullyAuthenticated()True if user is fully authenticated (not remember-me)
isRememberMe()True if authenticated via remember-me
hasIpAddress('IP')True if request comes from specified IP address
#paramNameAccess method parameter by name
returnObjectAccess method return value (in @PostAuthorize/@PostFilter)
filterObjectCurrent element in filtering operations
@beanName.method()Call method on Spring bean

Class-Level Annotations

Apply security annotations at class level:

@Service
@PreAuthorize("hasRole('ADMIN')")
public class AdminService {

    // All methods require ADMIN role by default

    public void method1() {
    }

    public void method2() {
    }

    // Override class-level annotation
    @PreAuthorize("hasRole('SUPER_ADMIN')")
    public void superAdminMethod() {
    }

    // Override to allow all
    @PreAuthorize("permitAll()")
    public void publicMethod() {
    }
}

Custom Security Expressions

Create custom security expressions:

@Component("customSecurity")
public class CustomSecurityExpressionService {

    public boolean hasPermission(Authentication authentication, Object targetObject, String permission) {
        // Custom permission check logic
        return permissionEvaluator.hasPermission(authentication, targetObject, permission);
    }

    public boolean isOwner(Authentication authentication, Long resourceId) {
        String username = authentication.getName();
        Resource resource = resourceRepository.findById(resourceId);
        return resource != null && resource.getOwner().equals(username);
    }

    public boolean canAccessDepartment(Authentication authentication, String departmentCode) {
        UserDetails user = (UserDetails) authentication.getPrincipal();
        return user.getDepartments().contains(departmentCode);
    }
}

// Usage
@Service
public class ResourceService {

    @PreAuthorize("@customSecurity.isOwner(authentication, #id)")
    public void updateResource(Long id, Resource resource) {
    }

    @PreAuthorize("@customSecurity.canAccessDepartment(authentication, #deptCode)")
    public List<Employee> getEmployees(String deptCode) {
    }
}