DEV Community

Isaac Tonyloi - SWE
Isaac Tonyloi - SWE

Posted on

Learn the basics of Spring Security

Introduction to Spring Security

Spring Security is a powerful and customizable authentication and access-control framework for Java applications, particularly those built with Spring. It is a de facto standard for securing Spring-based applications and provides a comprehensive range of security services, including authentication, authorization, and protection against common attacks like CSRF (Cross-Site Request Forgery).

Core Concepts of Spring Security

  1. Authentication:

    • What it is: The process of verifying the identity of a user or system. In Spring Security, this involves validating credentials such as a username and password.
    • How it works: The user submits credentials, which are authenticated by the system. If valid, the user is granted an Authentication object stored in the SecurityContext.
  2. Authorization:

    • What it is: The process of determining whether a user has permission to perform a certain action or access a resource.
    • How it works: Once authenticated, Spring Security checks if the user has the required roles or authorities to access specific resources or perform certain actions.
  3. SecurityContext:

    • What it is: A container that holds the security information of the current user, including their Authentication object.
    • How it works: The SecurityContext is stored in a ThreadLocal and is available throughout the processing of the user’s request.
  4. Filter Chain:

    • What it is: A series of filters that process incoming HTTP requests and outgoing responses to apply security logic.
    • How it works: The filter chain intercepts every request, applying filters such as AuthenticationFilter, AuthorizationFilter, and others to ensure security requirements are met.
  5. WebSecurityConfigurerAdapter:

    • What it is: A base class that provides a convenient way to configure Spring Security for your application.
    • How it works: By extending WebSecurityConfigurerAdapter, you can override methods to customize security settings such as authentication, authorization, and HTTP security.
  6. UserDetailsService:

    • What it is: A core interface that loads user-specific data. It is used to retrieve user information during authentication.
    • How it works: You implement this interface to fetch user details (such as username, password, roles) from a database or other source.
  7. Password Encoding:

    • What it is: The process of hashing passwords to store them securely.
    • How it works: Spring Security provides various PasswordEncoder implementations like BCryptPasswordEncoder to hash passwords before storing them and to validate during login.
  8. CSRF Protection:

    • What it is: Protection against Cross-Site Request Forgery attacks, where an attacker tricks a user into performing actions they didn't intend to.
    • How it works: Spring Security includes CSRF protection by default, requiring a unique token to be sent with each HTTP request that modifies state.

Setting Up Spring Security

Let’s walk through a simple Spring Boot application setup with Spring Security.

Step 1: Add Spring Security Dependency

First, add Spring Security to your Spring Boot project. If you are using Maven, add the following dependency to your pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode

This will bring in the necessary libraries to secure your application.

Step 2: Basic Configuration

By default, Spring Security applies basic security to all HTTP endpoints. When you start your Spring Boot application with Spring Security, it automatically secures all endpoints with a generated password and the username "user".

You can see this by running your application and navigating to any endpoint in the browser; you'll be prompted to log in.

Step 3: Customizing Security Configuration

To customize the security settings, you need to create a configuration class that extends WebSecurityConfigurerAdapter.

Here’s a basic example:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // In-memory authentication with hardcoded users
        auth.inMemoryAuthentication()
            .withUser("user")
            .password("password")
            .roles("USER")
            .and()
            .withUser("admin")
            .password("admin")
            .roles("ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Configuring which endpoints are secured and which are public
        http.authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .antMatchers("/user/**").hasRole("USER")
            .antMatchers("/").permitAll()
            .and().formLogin(); // Enables form-based login
    }

    @Bean
    public PasswordEncoder getPasswordEncoder() {
        // For demonstration purposes, we are using NoOpPasswordEncoder
        return NoOpPasswordEncoder.getInstance();
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. In-Memory Authentication:

    • The configure(AuthenticationManagerBuilder auth) method sets up in-memory authentication with hardcoded users. Each user is associated with a role (USER, ADMIN).
    • In a production application, you would typically fetch user details from a database instead of hardcoding them.
  2. Authorization Configuration:

    • The configure(HttpSecurity http) method configures the security rules:
      • /admin/**: Only accessible by users with the ADMIN role.
      • /user/**: Only accessible by users with the USER role.
      • /: Public endpoint, accessible by anyone without authentication.
    • formLogin() enables form-based login, where users are redirected to a login page if they are not authenticated.
  3. Password Encoding:

    • The PasswordEncoder bean is defined to specify how passwords should be encoded. In this example, NoOpPasswordEncoder is used, which does not apply any encoding (not secure for production).

Step 4: Form-Based Authentication

By default, Spring Security provides a login page when form-based authentication is enabled. Users are redirected to this login page if they try to access a secured endpoint.

You can customize this form by specifying your own login page like this:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/admin/**").hasRole("ADMIN")
        .antMatchers("/user/**").hasRole("USER")
        .antMatchers("/").permitAll()
        .and()
        .formLogin()
        .loginPage("/my-login")
        .permitAll();  // Allow everyone to see the login page
}
Enter fullscreen mode Exit fullscreen mode

You would need to create a controller and a view (my-login.html) to handle this custom login page.

Step 5: Protecting Against CSRF Attacks

Spring Security enables CSRF protection by default, which is particularly important for applications that handle form submissions. When CSRF protection is enabled, Spring Security expects a CSRF token to be included in every HTTP request that can change state (like POST, PUT, DELETE).

If you need to disable CSRF (not recommended for most applications), you can do so in the security configuration:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
        .authorizeRequests()
        .antMatchers("/admin/**").hasRole("ADMIN")
        .antMatchers("/user/**").hasRole("USER")
        .antMatchers("/").permitAll()
        .and().formLogin();
}
Enter fullscreen mode Exit fullscreen mode

Step 6: Role-Based Authorization

Spring Security uses roles to determine what a user is allowed to do. In the configuration above, we used .hasRole("ROLE") to restrict access to certain URLs.

You can also use role-based annotations in your controllers:

import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @Secured("ROLE_USER")
    @GetMapping("/user/home")
    public String userHome() {
        return "Welcome User!";
    }

    @Secured("ROLE_ADMIN")
    @GetMapping("/admin/home")
    public String adminHome() {
        return "Welcome Admin!";
    }
}
Enter fullscreen mode Exit fullscreen mode

In this example, the @Secured annotation restricts access to specific methods in your controllers based on user roles.

Conclusion

Spring Security is a comprehensive and flexible framework for securing Spring-based applications. By using it, you can handle authentication, authorization, password management, CSRF protection, and more with minimal configuration. The core components such as WebSecurityConfigurerAdapter, UserDetailsService, and SecurityContext work together to provide robust security while allowing for extensive customization to fit your specific needs.

Top comments (0)