Securing Your Spring Boot API with Spring Security 6

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).

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *