Date: November 17, 2024
Security is critical for any API exposed to the web, and Spring Security is the go-to framework in the Spring ecosystem for implementing robust authentication and authorization. With the release of Spring Security 6, developers get improved support for modern security standards, better integration with Spring Boot 3, and enhanced developer experience with concise configuration options.
This post covers how to secure your Spring Boot 3 REST API using Spring Security 6 — from basic setup to role-based access control, including examples you can run and adapt.
What’s New in Spring Security 6?
- Java 17+ baseline: Uses modern Java features for cleaner code.
- Jakarta EE namespace: Moves to
jakarta.*
packages for compatibility with Jakarta EE 9+. - Simplified configuration: Lambda-based DSL for configuring HTTP security.
- Improved OAuth 2.0 and OpenID Connect support: Easier integration with external identity providers.
- Better alignment with Spring Boot 3’s upgrades.
Getting Started: Adding Dependencies
Add Spring Security to your project dependencies:
<!-- Maven -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Or with Gradle:
implementation 'org.springframework.boot:spring-boot-starter-security'
Basic HTTP Security Configuration
Spring Security 6 encourages using a bean of type SecurityFilterChain
for configuration. Here’s a minimal example that secures all endpoints and enables HTTP Basic authentication:
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
return http.build();
}
}
This configuration requires users to authenticate for every endpoint using basic auth.
Defining Users and Roles
For demo and development, you can configure an in-memory user store:
@Bean
public UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
UserDetails admin = User.withDefaultPasswordEncoder()
.username("admin")
.password("adminpass")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
Note: withDefaultPasswordEncoder()
is suitable only for demos. Use bcrypt or other strong password encoders for production.
Role-Based Authorization Example
Let’s say you want to restrict access to /admin/**
endpoints only to users with the ADMIN
role:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
return http.build();
}
Securing REST APIs with JWT (JSON Web Tokens)
For stateless APIs, JWT is a popular authentication mechanism. Spring Security 6 offers better support for OAuth 2.0 Resource Servers and JWT out-of-the-box.
Add the OAuth 2 Resource Server dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
Configure your app to validate JWT tokens:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt());
return http.build();
}
You then configure JWT issuer and keys in your application.properties
or application.yml
for validation.
Password Encoding with BCrypt
Never store passwords as plain text. Use PasswordEncoder
for hashing:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Update your UserDetailsService
to encode passwords accordingly.
CSRF Protection for Browser Clients
Spring Security enables CSRF protection by default. For pure REST APIs consumed by non-browser clients, you might want to disable it:
http.csrf(csrf -> csrf.disable());
But always carefully assess the security implications.
Method-Level Security
Spring Security supports annotations to secure methods:
@EnableMethodSecurity
@Configuration
public class MethodSecurityConfig {}
Example:
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/data")
public ResponseEntity<String> adminData() {
return ResponseEntity.ok("Sensitive admin data");
}
Summary
Spring Security 6, together with Spring Boot 3, brings a clean, modern approach to securing Java applications:
- Lambda DSL simplifies HTTP security configuration.
- Seamless support for both basic auth and modern JWT OAuth 2.0.
- Role-based access control with fine granularity.
- Supports method-level security annotations.
- Aligns with modern Jakarta EE namespaces and Java 17+ features.
Next Steps
- Implement JWT token issuance with Spring Authorization Server (optional extra).
- Integrate OAuth2 login with Google, GitHub, or other providers.
- Add security logging and audit trails.
- Secure Actuator endpoints (covered in an earlier post).