In the world of web apps, users aren't just abstract "people". Each user can be assigned a role that defines what actions they can perform. This is a crucial part of security because it enforces strict separation of access to data and functionality.
Imagine you're building a banking app: a client can view their accounts, transfer funds, but can't, say, generate bank reports. An administrator, however, can. To manage this separation at the code level, we use a role-based access system. In Spring Security this is done with the @Secured annotation.
The @Secured annotation lets you control access to methods or resources based on user roles. You can use it to restrict access at the service, controller, or even individual business-method level.
Official docs: Spring Security: @Secured
Configuring roles and security using @Secured
Let's break this down step by step.
1. Project setup
Let's create a simple Spring Boot app where we'll work with users and roles. Make sure you have Spring Security on the classpath.
Add the dependencies to pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
Security configuration
To use @Secured you need to enable method-level security.
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true) // Enable @Secured support
public class SecurityConfig {
// In this lecture we use the built-in user configuration (InMemoryUserDetailsManager),
// We'll cover more advanced DB-backed examples later.
}
2. Define roles and users
For testing, let's create roles and users in memory (InMemoryUserDetailsManager).
Define users and roles:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
public class UserConfig {
@Bean
public InMemoryUserDetailsManager userDetailsManager() {
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin123"))
.roles("ADMIN") // Administrator role
.build();
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("user123"))
.roles("USER") // User role
.build();
return new InMemoryUserDetailsManager(admin, user);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); // We encrypt passwords
}
}
3. Using @Secured in services
Let's create a simple service and restrict access to its methods based on roles.
Implement the service:
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@Secured("ROLE_ADMIN") // Access for administrators only
public String adminMethod() {
return "Hey, Administrator!";
}
@Secured("ROLE_USER") // Access for users only
public String userMethod() {
return "Hey, User!";
}
}
Note that roles should be prefixed with ROLE_. For example, ROLE_ADMIN. That's the Spring Security convention.
4. Create a controller for testing
Now let's create a REST controller that uses our service.
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
private final MyService myService;
public MyController(MyService myService) {
this.myService = myService;
}
@GetMapping("/admin")
public String adminEndpoint() {
return myService.adminMethod();
}
@GetMapping("/user")
public String userEndpoint() {
return myService.userMethod();
}
}
5. Test the app
Run the app and test:
- Open Postman or just use your browser.
- Try to hit
/adminand/user.
If you log in as admin, access to /admin will be allowed, and /user — denied. For user — the opposite.
6. Add multiple roles to one method
If a method should be available to multiple roles, you can list them as an array:
@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
public String managerOrAdminMethod() {
return "Hey, Manager or Administrator!";
}
Spring Security will check whether the user has at least one of the listed roles.
Gotchas and common mistakes
- Roles must have the
ROLE_prefix. Spring Security expects roles to start withROLE_. If you don't do this, it won't work. For example, usingADMINinstead ofROLE_ADMINwon't work. - Getting a
403 Forbidden. If you see this error, make sure that:@Securedsupport is enabled via@EnableGlobalMethodSecurity(securedEnabled = true).- The user's role is specified correctly (including the
ROLE_prefix).
- Don't put role logic in controllers without a good reason. It's better to enforce security at the service layer. That helps minimize duplicated logic.
Practical usage
Using roles and access rights is a basic but very powerful tool for controlling access in applications. It's used everywhere: from small online stores to complex enterprise systems. So understanding how @Secured works isn't just useful — it's essential.
For more advanced cases we'll cover @PreAuthorize and @PostAuthorize in upcoming lectures — they let you build more flexible access rules using SpEL (Spring Expression Language).
GO TO FULL VERSION