LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 2720 min read

Securing APIs with Spring Security Basics

Add the Spring Security starter to a Spring Boot application, understand its default login behavior, and write a basic SecurityFilterChain configuration.

Introduction

An API with no security is one bad day away from a serious incident. Spring Security is the standard way to add authentication and authorization to a Spring Boot application, and Boot's auto-configuration makes it remarkably easy to get a reasonable baseline of protection with almost no code. In this lesson you will add the security starter, see what happens by default the moment it is on the classpath, and write your own SecurityFilterChain to control access explicitly.

What You Will Learn
  • What changes the instant spring-boot-starter-security is added.
  • Spring Boot's default login behavior.
  • How to write a basic SecurityFilterChain bean.
  • Why passwords must always be encoded, never stored as plain text.
  • How CSRF protection interacts with stateless REST APIs.

Adding the Security Starter

Add spring-boot-starter-security to your dependencies. Unlike most starters, this one changes application behavior immediately - simply having it on the classpath locks down every endpoint by default.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
This Dependency Changes Behavior Immediately

The moment this starter is added, every endpoint that was previously open now requires authentication. Do not add it to a project and walk away without testing - existing API calls will start returning 401 Unauthorized.

Default Login Behavior

With no configuration at all, Spring Boot generates a random password at startup, prints it to the console, and secures every endpoint with HTTP Basic and a form login, using a default username of user.

Startup Log

Click Run to see what this code prints.

curl -u user:8f2c1a9e-4b77-4e5a-9c31-77a0c9a441de http://localhost:8080/api/tasks

This default is only ever meant as a signal that security is active and working - it is never something to rely on beyond initial testing. Real applications define their own users (or delegate to an identity provider) and their own access rules.

A Basic SecurityFilterChain

Modern Spring Security configuration is done by declaring a SecurityFilterChain bean rather than extending a base configuration class. The example below allows public access to authentication endpoints while requiring a valid login for everything else.

@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/actuator/health").permitAll()
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.csrf(csrf -> csrf.disable());
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Unauthenticated Request

Click Run to see what this code prints.

Authenticated Request

Click Run to see what this code prints.

Password Encoding

Passwords must never be stored in plain text. Spring Security's PasswordEncoder abstraction hashes passwords with a strong, salted algorithm; BCryptPasswordEncoder is the standard default choice.

@Service
public class UserRegistrationService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public UserRegistrationService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
public User register(String username, String rawPassword) {
User user = new User();
user.setUsername(username);
user.setPassword(passwordEncoder.encode(rawPassword));
return userRepository.save(user);
}
}
Never Store or Log Plain-Text Passwords

Always call passwordEncoder.encode() before persisting a password, and never log the raw value anywhere - not even at DEBUG level during development.

CSRF and Stateless APIs

CSRF (Cross-Site Request Forgery) protection is designed for browser-based, session-authenticated applications where a form submission could be forged by another site. A stateless REST API authenticated with tokens (such as JWT) rather than cookies is not vulnerable to CSRF in the same way, which is why the example above disables it - but that decision should be deliberate, not accidental.

Application StyleCSRF Protection
Server-rendered pages with session cookiesKeep enabled (Spring Security's default)
Stateless REST API with token-based authCommonly disabled, since there is no session cookie to forge

Common Mistakes

Avoid These Mistakes
  • Shipping the auto-generated startup password to production instead of defining real users.
  • Storing passwords with NoOpPasswordEncoder or plain text instead of BCrypt.
  • Disabling CSRF without understanding why it was safe to do so for that specific application.
  • Using .anyRequest().permitAll() while debugging and forgetting to revert it before deploying.
  • Putting authorization rules in the controller instead of centralizing them in the SecurityFilterChain.

Best Practices

  • Define explicit rules for every path group rather than relying on the default lockdown behavior.
  • Always encode passwords with BCryptPasswordEncoder or a similarly strong algorithm.
  • Keep authentication endpoints (login, register) public and lock down everything else by default.
  • Make CSRF decisions deliberately based on whether the API is stateless or session-based.
  • Review the SecurityFilterChain configuration whenever new endpoints are added.

Frequently Asked Questions

No. HTTP Basic is a simple starting point. Real-world APIs commonly use token-based authentication such as JWT, or delegate entirely to an external identity provider through OAuth2/OIDC.

Adding spring-boot-starter-security secures every endpoint by default as soon as it is on the classpath. You must explicitly permit any endpoint that should remain public in your SecurityFilterChain.

Yes, BCrypt remains a solid, widely trusted choice for password hashing. Spring Security also supports newer algorithms like Argon2 via DelegatingPasswordEncoder if a project specifically needs them.

Key Takeaways

  • Adding spring-boot-starter-security immediately secures every endpoint by default.
  • A SecurityFilterChain bean is the modern way to declare authorization rules.
  • Passwords must always be hashed with a PasswordEncoder like BCrypt, never stored in plain text.
  • CSRF protection matters for session-based apps but is commonly disabled for stateless, token-based APIs.
  • Never rely on the auto-generated startup password beyond initial local testing.

Summary

Spring Security gives Boot applications a secure-by-default posture the moment the starter is added, and a SecurityFilterChain bean is where you take explicit control of what is public and what requires authentication. The next lesson builds on this foundation with a real login and authentication flow.

Next Lesson →

Building a Login/Authentication Flow