Spring Boot - Security Practices

Last Updated : 9 Oct, 2026

Spring Boot provides security features through Spring Security, a framework for implementing authentication, authorization, and protection against common web vulnerabilities. Securing a Spring Boot application involves protecting user credentials, restricting access, validating inputs, and securing communication between components.

  • Spring Security supports authentication, role-based authorization, and protection against common web attacks.
  • Secure applications require proper password handling, input validation, HTTPS, and API protection.
  • Monitoring security events and restricting sensitive endpoints help reduce security risks in production.

Security Best Practices in Spring Boot

Below are some best practices for securing Spring Boot applications:

1. Audit and Log Security Events

Security audits are essential for evaluating an organization’s security posture. Monitoring and logging security-related events, such as successful and failed login attempts, can provide valuable insights into potential threats.

Java
import org.springframework.context.event.EventListener;
import org.springframework.security.authentication.event.AbstractAuthenticationEvent;
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
import org.springframework.security.authentication.event.AbstractAuthenticationSuccessEvent;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AuditEventListener {

    private static final Logger logger = LoggerFactory.getLogger(AuditEventListener.class);

    @EventListener
    public void onApplicationEvent(AbstractAuthenticationEvent authEvent) {
        if (authEvent instanceof AbstractAuthenticationSuccessEvent) {
            handleSuccessEvent((AbstractAuthenticationSuccessEvent) authEvent);
        } else if (authEvent instanceof AbstractAuthenticationFailureEvent) {
            handleFailureEvent((AbstractAuthenticationFailureEvent) authEvent);
        }
    }

    private void handleSuccessEvent(AbstractAuthenticationSuccessEvent event) {
        WebAuthenticationDetails details = (WebAuthenticationDetails) event.getAuthentication().getDetails();
        logger.info("Authentication successful for user: {}, from IP: {}",
                    event.getAuthentication().getName(), details.getRemoteAddress());
    }

    private void handleFailureEvent(AbstractAuthenticationFailureEvent event) {
        WebAuthenticationDetails details = (WebAuthenticationDetails) event.getAuthentication().getDetails();
        logger.warn("Authentication failed for user: {}, from IP: {}",
                    event.getAuthentication().getName(), details.getRemoteAddress());
    }
}
  • @EventListener registers the method to listen for Spring authentication events.
  • The handleSuccessEvent method logs successful authentication attempts, while handleFailureEvent logs failed attempts.
  • Logging these events helps monitor and audit security incidents effectively.

2. Encrypt Sensitive Data

Sensitive data, such as passwords and confidential properties, should always be encrypted. You can use Spring's @EncryptablePropertySource annotation to manage encrypted property files.

Java
import org.springframework.context.annotation.Configuration;
import com.ulisesbocchio.jasyptspringboot.annotation.EncryptablePropertySource;

@EncryptablePropertySource(name = "SecureProperties", value = "classpath:secure-encrypted.properties")
@Configuration
public class SecureEncryptionConfig {
    // Additional beans and configurations for encryption can be added here
}
  • @EncryptablePropertySource decrypts property values when accessed from the specified file.
  • SecureEncryptionConfig configures the encryption setup, ensuring that sensitive properties are securely managed.

3. Use Parameterized Queries

Prevent SQL Injection attacks by using parameterized queries or JPA repositories, which automatically handle query parameters safely.

Java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepo extends JpaRepository<User, Long> {

    @Query("FROM User u WHERE u.emailAddress = :email")
    User getUserByEmail(@Param("email") String email);
}
  • The @Query annotation with named parameters ensures that the query is parameterized, preventing SQL injection.
  • Using JPA repositories for database access abstracts and secures database interactions.

4. Secure REST APIs with JWT

For stateless authentication in REST APIs, JSON Web Tokens (JWT) are commonly used. Below is an example of a JWT-based authentication filter:

Java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final UserDetailsService userDetailsService;
    private final String secretKey = "yourSecretKey";  // Store securely in production

    public JwtAuthenticationFilter(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, 
                                            FilterChain chain, UsernamePasswordAuthenticationToken authResult) 
                                            throws IOException, ServletException {
        String username = authResult.getName();
        Claims claims = Jwts.claims().setSubject(username);
        String token = Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 86400000))  // 1 day expiration
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
        response.addHeader("Authorization", "Bearer " + token);
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, 
                                              FilterChain chain, AuthenticationException failed) 
                                              throws IOException, ServletException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    }
}
  • JwtAuthenticationFilter manages the generation and validation of JWTs.
  • On successful authentication, a JWT is created and added to the response header.
  • The secretKey should be securely stored and managed in production.

5. Implement HTTPS

Securing data in transit is critical, especially for sensitive user information. Enforce HTTPS in your Spring Boot application.

Java
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .requiresChannel()
                .requestMatchers(request -> request.getHeader("X-Forwarded-Proto") != null)
                .requiresSecure()
                .and()
            .authorizeRequests()
                .antMatchers("/home").permitAll()  // Allow public access to the home endpoint
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .and()
            .httpBasic()
                .and()
            .csrf();  // CSRF protection enabled by default
    }
}
  • The requiresChannel().requiresSecure() enforces HTTPS.
  • CSRF protection and form-based login are enabled to protect against common attacks.

6. Thoroughly Validate Input

Always validate user input to protect against common vulnerabilities like SQL injection and cross-site scripting (XSS).

Java
import javax.validation.constraints.NotBlank;

public class UserRequest {

    @NotBlank(message = "The name field must not be blank")
    private String fullName;

    // Getters and Setters
    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }
}
  • @NotBlank ensures the fullName field is not empty or null.
  • Validating user input at this level prevents many common attacks.

7. Input Validation for Email

Utilize built-in validation annotations to ensure user input meets expected formats, particularly for fields like email.

Java
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;

public class UserData {

    @NotEmpty(message = "Username cannot be empty")
    private String userName;

    @Email(message = "Please provide a valid email address")
    private String emailAddress;

    // Constructor
    public UserData(String userName, String emailAddress) {
        this.userName = userName;
        this.emailAddress = emailAddress;
    }

    // Getters and Setters
    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    // toString method
    @Override
    public String toString() {
        return "UserData{" +
                "userName='" + userName + '\'' +
                ", emailAddress='" + emailAddress + '\'' +
                '}';
    }
}
  • @NotEmpty ensures that the userName field is not empty.
  • @Email validates the format of the emailAddress field.
  • These annotations help enforce data integrity and prevent invalid or potentially harmful input.

8. Secure Actuator Endpoints

Spring Boot Actuator provides production-ready features like health checks and metrics but exposes sensitive endpoints. Protect these by restricting access.

Java
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/actuator/**").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
            .httpBasic();
    }
}
  • The /actuator/** endpoints are restricted to users with the ADMIN role.
  • Using basic authentication adds an extra layer of protection to these endpoints.
Comment

Explore