Spring Security provides a powerful mechanism for configuring security via annotations. This makes our code not only more compact but also easier to read. Annotations let you restrict access to resources and methods right where they’re used.
In this lecture we'll go through four main annotations:
@Secured@RolesAllowed@PreAuthorize@PostAuthorize
Annotation @Secured
The @Secured annotation declares the roles that are allowed to access a method or resource. It's simple and convenient if you need a quick way to restrict access based on roles.
@Secured checks whether the user has a specific role (for example, ROLE_USER) so they can call the given method.
Example:
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@Secured("ROLE_ADMIN")
public void adminOnlyMethod() {
System.out.println("This method is only available to admins!");
}
@Secured({"ROLE_USER", "ROLE_ADMIN"})
public void userOrAdminMethod() {
System.out.println("This method is available to users and admins.");
}
}
@EnableGlobalMethodSecurity with the parameter
securedEnabled = true in your configuration.
How to enable it? Add the following setting to your security configuration class:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true) // Enable support for @Secured
public class SecurityConfig {
// You can add additional configurations here
}
Annotation @RolesAllowed
@RolesAllowed is an annotation from the JSR-250 standard that's very similar to @Secured. It also checks user roles, but it's a bit more "official" in the context of Java standards.
Usage example:
import javax.annotation.security.RolesAllowed;
import org.springframework.stereotype.Service;
@Service
public class AnotherService {
@RolesAllowed("ROLE_MANAGER")
public String managerOnly() {
return "Available only to managers!";
}
}
Enabling @RolesAllowed. To make this annotation work, enable it in your security configuration:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@Configuration
@EnableGlobalMethodSecurity(jsr250Enabled = true) // Enable support for @RolesAllowed
public class SecurityConfig {
}
@Secured and
@RolesAllowed together, you need to enable both parameters (
securedEnabled = true and
jsr250Enabled = true).
Annotation @PreAuthorize
This is a more powerful annotation that lets you use SpEL (Spring Expression Language) to define access conditions. You can check not only roles but also values of attributes of the object passed into the method.
For example, you can write a condition like "access is allowed only to users whose email ends with @example.com".
Example:
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
@PreAuthorize("hasRole('ROLE_USER')")
public void generalAccess() {
System.out.println("This method is only available to users with the ROLE_USER role.");
}
@PreAuthorize("#email.endsWith('@example.com')")
public void sendEmail(String email) {
System.out.println("Sending email to: " + email);
}
}
hasRole, while the second uses an expression that depends on the method parameter.
Enabling @PreAuthorize
To enable support for this annotation, add to your configuration:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // Enable support for @PreAuthorize and @PostAuthorize
public class SecurityConfig {
}
Annotation @PostAuthorize
How is it different from @PreAuthorize? If @PreAuthorize checks access before calling the method, then @PostAuthorize does it after the method executes. This is useful when you want to inspect the method result before returning it to the caller.
Example:
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.stereotype.Service;
@Service
public class DocumentService {
@PostAuthorize("returnObject.owner == authentication.name")
public Document getDocumentById(Long id) {
Document document = findDocumentById(id); // Document lookup logic
return document; // Check that the current user is the owner of the document
}
}
In this example the permission to return the document is checked after the method found it. If the current user isn't the document owner, Spring Security will throw an exception.
Role-based Access Control (RBAC)
Now that we've covered the annotations, let's talk about how this ties into role-based access control.
In RBAC, users are assigned specific roles (for example, ROLE_USER, ROLE_ADMIN), and those roles are granted access to certain resources. This lets you manage permissions effectively by centralizing rules.
Practice: Setting up security with annotations
Let's create a service where users can:
- See the list of their tasks (available only to users with the role
ROLE_USER). - Delete a task (available only to admins).
public class Task {
private Long id;
private String name;
private String owner;
// Getters and Setters
}
Service:
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class TaskService {
private List<Task> tasks = new ArrayList<>();
public TaskService() {
// Add a few tasks for example
tasks.add(new Task(1L, "Buy milk", "user1"));
tasks.add(new Task(2L, "Do homework", "user2"));
}
@PreAuthorize("hasRole('ROLE_USER')")
public List<Task> getUserTasks(String owner) {
// Logic for filtering tasks by owner
return tasks.stream()
.filter(task -> task.getOwner().equals(owner))
.toList();
}
@Secured("ROLE_ADMIN")
public void deleteTask(Long id) {
// Task deletion logic
tasks.removeIf(task -> task.getId().equals(id));
}
}
Controller:
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/tasks")
public class TaskController {
private final TaskService taskService;
public TaskController(TaskService taskService) {
this.taskService = taskService;
}
@GetMapping
public List<Task> getTasks(@RequestParam String owner) {
return taskService.getUserTasks(owner);
}
@DeleteMapping("/{id}")
public void deleteTask(@PathVariable Long id) {
taskService.deleteTask(id);
}
}
Now we've configured authorization both via roles and via expressions.
Common mistakes
- Forgot to enable annotations: if you don't enable
@EnableGlobalMethodSecurity, none of the annotations will work. - User has no roles: if a user has no roles assigned, all protected methods will be unavailable to them.
- Mixing different annotations: if you use both
@PreAuthorizeand@Secured, don't forget to enable support for both parameters.
Now you know how to configure security with annotations. It's a powerful tool that makes managing access in your apps way easier.
GO TO FULL VERSION