Skip to main content

Why Spring Security Locks All Endpoints Just By Adding the Dependency

Β· 8 min read
Mahmut Salman
Software Developer

"I just added spring-boot-starter-security to my pom.xml and now ALL my endpoints return 401 Unauthorized! I didn't write any security code - why is this happening?" This is Spring Security's "secure by default" philosophy at work. Let's understand why this design choice makes sense and how to configure it.

The Surprise​

You add one dependency:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

Restart your server, and suddenly:

curl http://localhost:8080/api/products
# Before: 200 OK βœ…
# After: 401 Unauthorized ❌

What just happened?


Spring Security Auto-Configuration​

When you add spring-boot-starter-security, Spring Boot's auto-configuration kicks in and automatically configures security for your entire application.

What Spring Does Behind the Scenes​

Spring Security creates something equivalent to this configuration automatically:

@Configuration
public class DefaultSecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated() // ← ALL requests require authentication
.and()
.httpBasic(); // ← Use HTTP Basic Authentication

return http.build();
}
}

Breaking it down:

  1. .authorizeRequests() - Start defining authorization rules
  2. .anyRequest().authenticated() - Every single request must be authenticated
  3. .httpBasic() - Use HTTP Basic Auth (username/password in headers)

Result: All endpoints now require authentication by default!


Why "Secure by Default"?​

This might seem inconvenient, but it's a brilliant security design:

Security Principle: Fail-Safe Defaults​

Better to start restrictive and open up, rather than start open and forget to secure things.

The Alternative (Dangerous)​

Imagine if Spring Security started with "allow everything":

// ❌ If Spring Security did this (it doesn't!)
http
.authorizeRequests()
.anyRequest().permitAll() // Everything open by default

What could go wrong:

@RestController
@RequestMapping("/api")
public class UserController {

@GetMapping("/users")
public List<User> getAllUsers() {
return userRepository.findAll(); // Exposes all user data!
}

@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable Long id) {
userRepository.deleteById(id); // Anyone can delete users!
}

@GetMapping("/admin/reports")
public Report getFinancialReport() {
return reportService.getFinancialData(); // Sensitive data exposed!
}
}

If you forget to add @PreAuthorize or security config, these endpoints would be wide open!

With "Secure by Default"​

// Spring Security's approach
http
.authorizeRequests()
.anyRequest().authenticated() // Everything locked by default

Now:

  • You must explicitly open endpoints you want public
  • You can't accidentally expose sensitive data
  • Forgotten endpoints remain protected

Security mindset: Whitelist (allow specific things) > Blacklist (block specific things)


The Complete Auto-Configuration​

When you add spring-boot-starter-security, Spring Boot auto-configures:

1. Security Filter Chain​

All HTTP requests pass through security filters:

Client Request
↓
SecurityFilterChain (intercepts)
↓
Check: Is user authenticated?
↓
YES β†’ Allow request β†’ Controller
NO β†’ Return 401 Unauthorized

2. Default User Credentials​

Spring generates a random password on startup:

Console output:

Using generated security password: 8e557245-73e2-4286-969a-ff57fe326d53

Default credentials:

  • Username: user
  • Password: 8e557245-73e2-4286-969a-ff57fe326d53 (random, changes every restart)

3. HTTP Basic Authentication​

Authentication header format:

Authorization: Basic dXNlcjpwYXNzd29yZA==

(Base64 encoding of user:password)


Testing Protected Endpoints​

Before Spring Security​

curl http://localhost:8080/api/products
# 200 OK
# [{"id":1,"name":"Laptop",...}]

After Spring Security​

# Without credentials - 401 Unauthorized
curl http://localhost:8080/api/products
# 401 Unauthorized

# With credentials - 200 OK
curl -u user:8e557245-73e2-4286-969a-ff57fe326d53 \
http://localhost:8080/api/products
# 200 OK
# [{"id":1,"name":"Laptop",...}]

In Postman​

  1. Select Authorization tab
  2. Type: Basic Auth
  3. Username: user
  4. Password: 8e557245-73e2-4286-969a-ff57fe326d53 (from console)
  5. Send request β†’ 200 OK βœ…

Customizing Security Configuration​

Option 1: Set Fixed Credentials​

application.properties:

spring.security.user.name=admin
spring.security.user.password=admin123

Now you can use admin:admin123 instead of the random password.

Note: Only for development! Never use hardcoded passwords in production.

Option 2: Allow Public Endpoints​

Create a SecurityConfig class to customize which endpoints are public:

package com.ecommerce.app.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/test").permitAll() // βœ… Public endpoint
.antMatchers("/api/products").permitAll() // βœ… Public endpoint
.antMatchers("/api/admin/**").authenticated() // πŸ”’ Requires auth
.anyRequest().authenticated() // πŸ”’ Everything else requires auth
.and()
.httpBasic();

return http.build();
}
}

Now:

  • /api/test β†’ Public (no auth needed)
  • /api/products β†’ Public (no auth needed)
  • /api/admin/reports β†’ Protected (auth required)
  • Everything else β†’ Protected (auth required)
@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().permitAll() // ⚠️ Allow everything (DANGEROUS!)
.and()
.csrf().disable();

return http.build();
}
}

Warning: Only use this for local development/testing. Never in production!


Common Configurations​

Public API + Protected Admin Endpoints​

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/public/**").permitAll() // Public API
.antMatchers("/api/admin/**").hasRole("ADMIN") // Admin only
.anyRequest().authenticated() // Everything else
.and()
.httpBasic();

return http.build();
}

Public Read, Protected Write​

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/**").permitAll() // Public reads
.antMatchers(HttpMethod.POST, "/api/**").authenticated() // Protected writes
.antMatchers(HttpMethod.PUT, "/api/**").authenticated() // Protected updates
.antMatchers(HttpMethod.DELETE, "/api/**").authenticated() // Protected deletes
.anyRequest().authenticated()
.and()
.httpBasic();

return http.build();
}

Disable CSRF for REST APIs​

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable() // Disable CSRF for stateless REST APIs
.authorizeRequests()
.antMatchers("/api/test").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();

return http.build();
}

Note: CSRF protection is important for browser-based apps but can be disabled for stateless REST APIs.


What Happens Step-by-Step​

1. Add Dependency​

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

2. Spring Boot Auto-Configuration Detects It​

Application startup
↓
Spring Boot scans classpath
↓
Finds spring-boot-starter-security
↓
Triggers SecurityAutoConfiguration
↓
Creates default SecurityFilterChain bean
↓
Applies default security rules

3. Security Filters Activate​

HTTP Request
↓
Servlet Filter Chain
↓
Spring Security Filters (intercept)
↓
Authentication Check
↓
Authorization Check
↓
Controller (if authorized)

4. Default Configuration Applied​

// Automatically configured by Spring
- All endpoints require authentication
- HTTP Basic Auth enabled
- Random password generated
- Username: "user"
- Password: printed to console

Real-World Example​

Before Spring Security​

@RestController
@RequestMapping("/api")
public class ProductController {

@GetMapping("/products")
public List<Product> getAllProducts() {
return productService.findAll();
// Accessible to everyone βœ…
}

@PostMapping("/products")
public Product createProduct(@RequestBody Product product) {
return productService.save(product);
// Accessible to everyone ⚠️ (dangerous!)
}

@DeleteMapping("/products/{id}")
public void deleteProduct(@PathVariable Long id) {
productService.delete(id);
// Accessible to everyone ⚠️ (dangerous!)
}
}

Problem: Anyone can create and delete products!

After Spring Security (Default)​

@RestController
@RequestMapping("/api")
public class ProductController {

@GetMapping("/products")
public List<Product> getAllProducts() {
return productService.findAll();
// Requires authentication πŸ”’
}

@PostMapping("/products")
public Product createProduct(@RequestBody Product product) {
return productService.save(product);
// Requires authentication πŸ”’
}

@DeleteMapping("/products/{id}")
public void deleteProduct(@PathVariable Long id) {
productService.delete(id);
// Requires authentication πŸ”’
}
}

Result: All endpoints protected by default!

After Custom Configuration​

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/products").permitAll() // βœ… Public
.antMatchers(HttpMethod.POST, "/api/products").authenticated() // πŸ”’ Protected
.antMatchers(HttpMethod.DELETE, "/api/products/**").authenticated() // πŸ”’ Protected
.anyRequest().authenticated()
.and()
.httpBasic();

return http.build();
}
}

Result:

  • GET /api/products β†’ Public (anyone can view)
  • POST /api/products β†’ Protected (only authenticated users)
  • DELETE /api/products/{id} β†’ Protected (only authenticated users)

Debugging Security Issues​

Check Security Configuration​

Enable security logging in application.properties:

logging.level.org.springframework.security=DEBUG

Console output:

Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CsrfFilter
LogoutFilter
BasicAuthenticationFilter ← Handles HTTP Basic Auth
...
]

Test Authentication​

# Test without auth - should fail
curl -v http://localhost:8080/api/products

# Test with auth - should succeed
curl -v -u admin:admin123 http://localhost:8080/api/products

Best Practices​

βœ… DO​

  1. Keep default "secure by default" behavior

    .anyRequest().authenticated()  // Good starting point
  2. Explicitly allow public endpoints

    .antMatchers("/api/public/**").permitAll()
  3. Use strong passwords in production

    // Use environment variables, not hardcoded values
    @Value("${ADMIN_PASSWORD}")
    private String adminPassword;
  4. Disable CSRF for REST APIs

    .csrf().disable()  // Only for stateless APIs

❌ DON'T​

  1. Don't disable security completely

    // ❌ Dangerous!
    .authorizeRequests().anyRequest().permitAll()
  2. Don't hardcode passwords

    // ❌ Bad practice
    spring.security.user.password=admin123
  3. Don't forget to configure public endpoints

    // ❌ Forces auth on login page too!
    .anyRequest().authenticated()
  4. Don't expose sensitive endpoints

    // ❌ Admin endpoints should not be public
    .antMatchers("/api/admin/**").permitAll()

Summary​

The Question​

"Why do my endpoints require authentication? I just added the dependency!"

The Answer​

Spring Security's "secure by default" philosophy:

  1. Auto-configuration triggers when spring-boot-starter-security is detected
  2. Default behavior: All endpoints require authentication (.anyRequest().authenticated())
  3. Why: Better to start restrictive and open up than start open and forget to secure
  4. Result: 401 Unauthorized for all endpoints until you configure security

Key Takeaways​

AspectBehavior
Default stateAll endpoints protected
AuthenticationHTTP Basic Auth enabled
CredentialsUsername: user, Password: random (printed to console)
PhilosophyFail-safe defaults (secure by default)
ConfigurationCustomize via SecurityConfig class

Configuration Options​

// Option 1: Allow specific endpoints
.antMatchers("/api/test").permitAll()

// Option 2: Set fixed credentials
spring.security.user.name=admin
spring.security.user.password=admin123

// Option 3: Custom SecurityFilterChain
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) { ... }

Bottom line: Spring Security protects you from accidentally exposing sensitive endpoints. It forces you to think about security from day one, which is exactly what good security design should do! πŸ›‘οΈ