Skip to main content

Java Spring security with custom authentications

24 Jan 2025

Security in modern web applications is critical because these applications often handle sensitive data, including personal information, financial details, and intellectual property. Any breach can lead to significant harm for users and businesses.

What is Java Spring security?

Spring Security is the de facto standard for securing Spring-based applications, offering comprehensive and customizable security features. Java Spring security seamlessly integrates with Spring applications, allowing developers to enforce security at various layers, including web, method, and domain levels. Its flexibility allows for integration with various authentication methods, such as OAuth2, LDAP, and JWT, making it highly adaptable to different security requirements. The framework is widely used due to its ease of configuration, scalability, and strong community support.

Why is Java Spring security necessary?

Java Spring Security is widely used in Spring-based applications due to several compelling reasons:

  • Comprehensive security features: It provides out-of-the-box features like authentication, authorization, password management, session management, and protection against common attacks such as Cross-Site Request Forgery (CSRF), Cross-Site Scripting (XSS), and Clickjacking.
  • Customizable and flexible: Spring Security is highly customizable, allowing developers to adapt the security configuration based on the specific needs of their application. Whether it’s custom authentication mechanisms, access control rules, or securing REST APIs, Spring Security can be easily extended to fit various scenarios.
  • Multiple authentication methods: It supports multiple Java Spring authentication providers, such as database-backed authentication, LDAP, OAuth2, JWT, SAML, and social login (like Google and Facebook). This flexibility allows developers to choose or combine various authentication strategies.
  • Declarative security with annotations: This allows developers to secure methods and endpoints declaratively using annotations like `@PreAuthorize`, `@Secured`, and `@RolesAllowed`, making security enforcement cleaner and easier to maintain.
  • Protection against common vulnerabilities: We get built-in defences against several common web vulnerabilities, including CSRF, XSS, and session fixation, reducing the risk of common attacks and improving the application's overall security posture.
  • Simplified Role-Based Access Control (RBAC): With easy role and authority management, Java Spring Security simplifies the implementation of RBAC, enabling fine-grained control over which users or groups can access specific resources or perform specific actions.

Steps to configure Spring security in applications

Let’s look at the process to configure Java Spring Security in applications
Dependency for spring-boot-security

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

Note: This dependency has default security configuration.

Once the dependency is added, Spring Security automatically applies the following default configurations:

  • All HTTP endpoints in your application are secured.
  • You must authenticate using HTTP Basic Authentication (username and password).
  • A default login page is generated by Spring Security if you access the app through a web browser.
  • No custom user credentials are needed at this stage, as Spring Boot automatically creates a default user with the username and generates a random password, which can be found in the console logs.

Steps to configure the customized authentication with basic RBAC.

Step 1: Create a class with name SecurityConfig  which is responsible to configuring the incoming request, and the withDefaults() basically enables a security feature using the defaults provided by Spring Security.
withDefaults() can be imported as below.

import static org.springframework.security.config.Customizer.withDefaults;

@Override
   protected void configure(HttpSecurity http) throws Exception {
       http
            .authorizeHttpRequests(auth -> auth
                    .anyRequest().authenticated()
            )
            .formLogin(withDefaults());
        return http.build();
   }

Step 2: Add PasswordEncoder method to encode the password.

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

Step 3: Define a User entity that will represent the user in the application. Here, we will use default InMemoryUserDetails which is provided by the Spring framework, and we will use the password encoder to encode the password.

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;

   @Bean
public UserDetailsService userDetailsService(PasswordEncoder encoder) {
    UserDetails admin = User.withUsername("user").password(encoder.encode("123")).roles("USER").build();
    UserDetails user = User.withUsername("admin").password(encoder.encode("123")).roles("ADMIN").build();
    return new InMemoryUserDetailsManager(admin, user);
}

Step 4: After we’ve configured the default USER model, we need to restrict the access to different parts of the application based on roles. This configuration ensures that certain URLs are accessible only by users with specific roles. This needs to be updated in the SecurityConfig class.

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http
        .authorizeHttpRequests(authz -> authz
            .requestMatchers("/admin/**").hasRole("ADMIN")
            .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
            .anyRequest().authenticated()
        );
   }

Step 5: Create one Rest controller for consuming the above configuration and enable method-level security using annotations`@PreAuthorize`

 @RestController
public class UserController {

@GetMapping("/welcome")
public String welcome() {
return "Welcome, this endpoint is not secure";
}    

@GetMapping("/user/dashboard")
@PreAuthorize("hasRole('USER')")
public String userProfile() {
return "Welcome to User Profile";
}

@GetMapping("/admin/dashboard")
@PreAuthorize("hasRole('ADMIN')")
public String adminProfile() {
return "Welcome to Admin Profile";
}
}

Test the Application

Now run your application and test it out. Hit the following URL.
http://localhost:8080/welcome

You can access this endpoint and will see the below message without any authentication as it is not secured.
Welcome, this endpoint is not secure

Now, hit the following URL:
http://localhost:8080/user/dashboard

If not logged in: You will be redirected to the below URL
http://localhost:8080/login

After entering the correct Username and Password you can access your endpoint. 
e.g.  Enter the following Username and Password:

Username: user
Password: 123

And you will get the below output message: 
Welcome to User Profile

What are the Java Spring Security best practices?

Enable CSRF protection: Cross-Site Request Forgery (CSRF) attacks can be devastating. Spring Security enables CSRF protection by default. Verify it's not mistakenly disabled:

http.csrf().disable();

Enable method level security: Method level authorization problems happen when an unauthorized or low-privileged user can access the sensitive functions of an application like functions normally scoped to administrator roles.

@PreAuthorize("hasRole('USER')")
public String userProfile() {
    // user profile logic
}

Password hashing

For enhanced security, it's highly recommended to use password hashing in your Spring Security application. Instead of storing plaintext passwords, use a strong hashing algorithm like bcrypt, which securely hashes passwords before storing them in the database. This ensures that even if an attacker gains access to the database, they cannot retrieve the original passwords. You can easily integrate this with Spring Security by using BCryptPasswordEncoder to hash passwords during registration and validate them during login. This simple measure significantly improves the security of user credentials. This needs to be added in configure class:

   @Bean
   public PasswordEncoder passwordEncoder() {
       return new BCryptPasswordEncoder();
   }

Conclusion

In this guide, we've explored the essential aspects of implementing Spring Security, covering foundational security concepts such as authentication and authorization. 
We will also explore advanced security features like OAuth2, JWT, Multi-Factor Authentication (MFA) and custom security filters in our next blog in this series. These features significantly enhance the security of your application by adding additional layers of protection and allowing you to implement highly customized security workflows.
By integrating these advanced features into your Spring Security configuration, you’ll be better prepared to safeguard your application against evolving security threats, ensuring a secure, user-friendly experience.

Subscribe to our feed

select webform