Getting Started with Spring Security: A Practical Guide for Spring Boot Developers
Backend • 5 min read

Getting Started with Spring Security: A Practical Guide for Spring Boot Developers

Julius Fasema

Author

Learn how to integrate Spring Security into Spring Boot applications with practical Java examples covering authentication, authorization, Maven dependencies, BCrypt passwords, and API security.

Security is one of the first concerns developers encounter when building a production-ready web application. An application may have a well-designed REST API, a powerful database and a modern frontend, but without proper authentication and authorization, its resources can easily be exposed to unauthorized users.

For Java developers working with Spring, Spring Security provides a comprehensive security framework for protecting web applications and APIs.

In this practical guide, we will set up Spring Security in a Spring Boot application, examine the dependencies involved, configure a simple security policy, and look at some of the dependency-management issues developers should understand before moving to more advanced authentication mechanisms such as JWT, OAuth 2.0 and LDAP.

What is Spring Security?

Spring Security is the security framework used to protect Spring-based applications.

It provides mechanisms for implementing common security requirements such as:

  • Authentication
  • Authorization
  • Password management
  • Role-based access control
  • Method-level security
  • Protection against common web attacks
  • OAuth 2.0
  • OpenID Connect
  • LDAP authentication
  • Resource-server security
  • Session management

One of its biggest advantages is that security can be integrated into a Spring application without developers having to implement authentication and authorization infrastructure from scratch.

For example, instead of writing custom code to determine whether every request should be allowed or rejected, Spring Security can intercept requests and apply a centrally defined security policy.

Creating a Spring Boot Project

The easiest way to start a Spring Security project is with Spring Initializr.

Open:

start.spring.io

Create a Maven-based Spring Boot application and add the following dependencies:

  • Spring Web
  • Spring Security

You can then generate and download the project.

A typical project might look like this:

The exact package structure is not important. What matters is that the application contains the Spring Boot application class, a controller and a security configuration.

Adding Spring Security to Maven

If you already have a Spring Boot application and want to add Spring Security manually, add the following dependency to pom.xml:

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-security</artifactId>

</dependency>

Notice that there is no version specified.

This is intentional.

Spring Boot manages compatible dependency versions through its dependency-management mechanism. As a result, developers normally do not need to manually specify the Spring Security version when using the Spring Boot starter.

Your dependencies might therefore look like this:

<dependencies>

 

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-web</artifactId>

    </dependency>

 

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-security</artifactId>

    </dependency>

 

</dependencies>

This is generally the preferred approach for a Spring Boot application.

What Happens When Spring Security Is Added?

This is one of the first things that surprises developers who are new to Spring Security.

Suppose we create the following controller:

@RestController

@RequestMapping("/api")

public class HelloController {

 

    @GetMapping("/hello")

    public String hello() {

        return "Hello from Code Digest";

    }

}

Without Spring Security, a request to:

GET /api/hello

would normally return:

Hello from Code Digest

Now add:

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-security</artifactId>

</dependency>

Start the application and visit the endpoint again.

You will find that the endpoint is no longer freely accessible.

Spring Boot automatically applies a default security configuration.

For a simple development application, Spring Boot can generate a default user and password-based authentication mechanism. The generated password is displayed in the application startup logs.

For example:

Using generated security password: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

This is useful for getting started, but it is not a production authentication strategy.

Creating a Security Configuration

For a real application, developers normally define their own security rules.

Create:

config/SecurityConfig.java

Then add:

package com.codedigest.securitydemo.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.web.SecurityFilterChain;

 

@Configuration

public class SecurityConfig {

 

    @Bean

    public SecurityFilterChain securityFilterChain(

            HttpSecurity http) throws Exception {

 

        http

            .authorizeHttpRequests(auth -> auth

                .requestMatchers("/api/public").permitAll()

                .anyRequest().authenticated()

            );

 

        return http.build();

    }

}

This small configuration already demonstrates one of the most important concepts in Spring Security.

We are defining which requests are public and which requests require authentication.

The rule:

.requestMatchers("/api/public").permitAll()

means that /api/public can be accessed without authentication.

The rule:

.anyRequest().authenticated()

means every other request must be authenticated.

Creating Public and Protected Endpoints

Let's modify the controller:

@RestController

@RequestMapping("/api")

public class HelloController {

 

    @GetMapping("/public")

    public String publicEndpoint() {

        return "This endpoint is public";

    }

 

    @GetMapping("/private")

    public String privateEndpoint() {

        return "This endpoint requires authentication";

    }

}

Now there are two endpoints:

GET /api/public

GET /api/private

The first one is public.

The second requires authentication.

This distinction is fundamental when building REST APIs.

For example, an e-commerce application might expose:

GET /api/products

publicly, while protecting:

GET /api/orders

POST /api/orders

DELETE /api/orders/{id}

behind authentication.

Understanding Authentication and Authorization

These two terms are often confused.

Authentication

Authentication answers:

Who are you?

For example, a user logs into an application using:

Email: user@example.com

Password: ********

The application verifies the credentials and establishes the user's identity.

Authorization

Authorization answers:

What are you allowed to do?

Suppose a user successfully logs in.

That does not automatically mean the user can access everything.

For example:

USER

 ├── View products

 ├── Create orders

 └── View own orders

 

ADMIN

 ├── View products

 ├── Create orders

 ├── View all orders

 └── Delete products

Authentication establishes the identity.

Authorization determines what that identity can access.

Spring Security supports both concepts.

Adding a Default User for Testing

For a simple demonstration, we can define an in-memory user.

Add the following to SecurityConfig:

@Bean

public UserDetailsService userDetailsService() {

 

    UserDetails user = User

        .withUsername("developer")

        .password("{noop}password")

        .roles("USER")

        .build();

 

    return new InMemoryUserDetailsManager(user);

}

The complete configuration becomes:

@Configuration

public class SecurityConfig {

 

    @Bean

    public SecurityFilterChain securityFilterChain(

            HttpSecurity http) throws Exception {

 

        http

            .authorizeHttpRequests(auth -> auth

                .requestMatchers("/api/public").permitAll()

                .anyRequest().authenticated()

            )

            .httpBasic();

 

        return http.build();

    }

 

    @Bean

    public UserDetailsService userDetailsService() {

 

        UserDetails user = User

            .withUsername("developer")

            .password("{noop}password")

            .roles("USER")

            .build();

 

        return new InMemoryUserDetailsManager(user);

    }

}

The {noop} prefix tells Spring Security that the password is stored without encoding.

This should only be used for demonstrations and testing.

Never store production passwords this way.

For a production application, use a password encoder such as BCrypt.

 

Using BCrypt for Passwords

A better configuration is:

@Bean

public PasswordEncoder passwordEncoder() {

    return new BCryptPasswordEncoder();

}

Then create the user using the encoder:

@Bean

public UserDetailsService userDetailsService(

        PasswordEncoder passwordEncoder) {

 

    UserDetails user = User

        .withUsername("developer")

        .password(passwordEncoder.encode("password"))

        .roles("USER")

        .build();

 

    return new InMemoryUserDetailsManager(user);

}

This gives us:

@Bean

public PasswordEncoder passwordEncoder() {

    return new BCryptPasswordEncoder();

}

and:

@Bean

public UserDetailsService userDetailsService(

        PasswordEncoder passwordEncoder) {

 

    UserDetails user = User

        .withUsername("developer")

        .password(passwordEncoder.encode("password"))

        .roles("USER")

        .build();

 

    return new InMemoryUserDetailsManager(user);

}

The important lesson is that applications should never store users' plaintext passwords.

Testing the Application

Start the Spring Boot application.

For example:

./mvnw spring-boot:run

Or, on Windows:

mvnw.cmd spring-boot:run

Now test the public endpoint:

curl http://localhost:8080/api/public

The response should be:

This endpoint is public

Now try:

curl http://localhost:8080/api/private

Because authentication is required, the request will not succeed without credentials.

Provide the username and password:

curl -u developer:password \

     http://localhost:8080/api/private

The response should be:

This endpoint requires authentication

We have therefore created a basic authenticated API without implementing the authentication mechanism ourselves.

Understanding spring-boot-starter-security

One dependency:

<artifactId>spring-boot-starter-security</artifactId>

pulls in the Spring Security components needed by a typical Spring Boot application.

This is an example of why Spring Boot starters are useful.

Instead of manually adding a collection of individual Spring Security libraries, the starter provides a convenient dependency entry point.

The application can then build on top of Spring Boot's dependency-management system.

What About Spring Security Versions?

Spring Security uses a:

MAJOR.MINOR.PATCH

versioning structure.

For example:

7.1.1

can be interpreted as:

7 = major

1 = minor

1 = patch

Major releases may introduce breaking changes.

Minor releases generally introduce enhancements while maintaining compatibility expectations.

Patch releases are primarily intended for compatible fixes.

This distinction becomes important when upgrading an existing application.

A major version upgrade should not be treated in exactly the same way as installing a routine patch release.

Should You Manually Specify the Spring Security Version?

For most Spring Boot applications:

No.

If Spring Boot manages the dependency version, let Spring Boot manage it.

For example:

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-security</artifactId>

</dependency>

is preferable to manually attaching a Spring Security version unless there is a specific reason to override the managed version.

Manually mixing versions can result in dependency conflicts.

For example, an application could accidentally combine:

Spring Boot version A

Spring Framework version B

Spring Security version C

when those versions were not intended to be used together.

That can produce errors that are difficult to diagnose because the application may compile successfully before failing during startup or runtime.

Overriding the Spring Security Version

There are situations where a developer may deliberately need a different Spring Security version.

Spring Boot's dependency management can be overridden through a property such as:

<properties>

    <spring-security.version>7.1.1</spring-security.version>

</properties>

However, this should be done deliberately.

Before overriding a managed dependency, check compatibility between:

  • Spring Boot
  • Spring Framework
  • Spring Security
  • Other Spring projects in the application

Dependency management is not simply about getting the newest version of every library.

It is about getting a compatible set of libraries.


Additional Spring Security Modules

The core starter is not the only part of Spring Security.

Applications requiring additional functionality may need additional modules.

For example, a project using OAuth 2.0 may require OAuth-related dependencies.

Similarly, an application integrating LDAP authentication may require LDAP support.

This follows a useful principle:

Add the Spring Security module that provides the feature your application actually needs.

A simple username/password application does not necessarily need every Spring Security module.

Using Spring Security Without Spring Boot

Spring Security can also be used without Spring Boot.

In that situation, developers have more responsibility for dependency management.

Instead of relying on the Spring Boot dependency-management system, Spring Security provides a Bill of Materials (BOM).

A Maven configuration can import the Spring Security BOM:

<dependencyManagement>

    <dependencies>

 

        <dependency>

            <groupId>org.springframework.security</groupId>

            <artifactId>spring-security-bom</artifactId>

            <version>{spring-security-version}</version>

            <type>pom</type>

            <scope>import</scope>

        </dependency>

 

    </dependencies>

</dependencyManagement>

Then individual Spring Security dependencies can be declared without repeating their versions:

<dependencies>

 

    <dependency>

        <groupId>org.springframework.security</groupId>

        <artifactId>spring-security-web</artifactId>

    </dependency>

 

    <dependency>

        <groupId>org.springframework.security</groupId>

        <artifactId>spring-security-config</artifactId>

    </dependency>

 

</dependencies>

The BOM helps keep the Spring Security modules on a consistent version.

Maven Central and Spring Security

For normal General Availability releases, developers typically do not need to add a special repository.

Maven Central is sufficient.

For example:

<repositories>

    <repository>

        <id>central</id>

        <url>https://repo.maven.apache.org/maven2</url>

    </repository>

</repositories>

In a standard Maven setup, however, Maven Central is already configured by default, so adding it manually is usually unnecessary.

What About SNAPSHOT Releases?

Developers experimenting with unreleased Spring versions may encounter SNAPSHOT dependencies.

A SNAPSHOT repository can be configured when required:

<repository>

    <id>spring-snapshot</id>

    <name>Spring Snapshot Repository</name>

    <url>https://repo.spring.io/snapshot</url>

</repository>

Similarly, milestone or release-candidate builds can require the Spring milestone repository.

These repositories are generally unnecessary for applications using normal stable releases.

For production systems, stable General Availability releases are normally the safer choice.

A Practical Spring Security Development Workflow

When starting a new Spring Boot application, a sensible workflow is:

Step 1 — Create the project

Use Spring Initializr.

Step 2 — Add Spring Security

Select:

Spring Security

Step 3 — Create your controllers

Start with the application functionality before adding complex security rules.

Step 4 — Define security boundaries

Identify:

Public endpoints

Authenticated endpoints

Admin endpoints

Step 5 — Configure SecurityFilterChain

For example:

@Bean

SecurityFilterChain securityFilterChain(HttpSecurity http)

        throws Exception {

    http

        .authorizeHttpRequests(auth -> auth

            .requestMatchers("/api/public/**").permitAll()

            .requestMatchers("/api/admin/**")

                .hasRole("ADMIN")

            .anyRequest()

                .authenticated()

        );

    return http.build();

}

This gives us three security levels:

/api/public/**   → anyone

/api/admin/**    → ADMIN role

everything else  → authenticated users

Step 6 — Choose the authentication mechanism

Depending on the application, this might be:

Session authentication

HTTP Basic

JWT

OAuth 2.0

OpenID Connect

LDAP

Step 7 — Test security independently

Do not test only the successful login path.

Also test:

  • Unauthenticated requests
  • Invalid credentials
  • Expired credentials
  • Insufficient roles
  • Access to protected resources
  • Access to administrative resources

Common Mistakes to Avoid

1. Hard-coding every dependency version

Avoid doing this unnecessarily:

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-security</artifactId>

    <version>...</version>

</dependency>

Let Spring Boot manage starter versions.

2. Storing plaintext passwords

Do not use:

.password("{noop}password")

in production.

Use an appropriate password encoder.

3. Making every endpoint public

Avoid:

.anyRequest().permitAll()

unless the application genuinely requires all endpoints to be public.

4. Mixing incompatible Spring versions

Do not randomly combine Spring Boot, Spring Framework and Spring Security versions.

Understand the dependency-management model first.

5. Assuming authentication means authorization

A successfully authenticated user should not automatically receive administrator privileges.

Define roles and authorities explicitly.

Conclusion

Adding Spring Security to a Spring Boot application can be remarkably simple. In many cases, all that is required to get started is:

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-security</artifactId>

</dependency>

The real work begins when defining how the application's resources should be protected.

Developers should understand the difference between authentication and authorization, identify public and protected resources, configure a SecurityFilterChain, and select an authentication mechanism appropriate for the application.

For Spring Boot projects, allowing Spring Boot to manage Spring Security's dependency versions is generally the simplest approach. For standalone Spring Security applications, the Spring Security BOM provides a way to keep related modules aligned.

From this foundation, developers can move toward more advanced security architectures involving BCrypt password storage, JWT, OAuth 2.0, OpenID Connect, LDAP and role-based access control.

The important lesson is that security should not be added as an afterthought. It should be treated as part of the application's architecture from the beginning.