Spring Security – Customizing Authentication and Authorization
Last Updated :
23 Sep, 2024
Spring Security is the powerful and customizable framework that provides the authentication, authorization, and other security features for the Java applications, especially the Spring-based ones. When building secure applications, controlling access to resources is important. Customizing authorization helps control access to specific endpoints or methods, ensuring that only authorized users can perform certain actions.
- Customizing authentication allows developers to define how users are authenticated.
- Customizing authorization helps control access to various resources based on user roles or permissions.
In this article, we will cover how to configure authentication and authorization in Spring Boot with some customization examples.
Customizing Authentication and Authorization in Spring Security
Spring Security provides flexible mechanisms for authentication (validating the identity of the user) and authorization (determining what resources or actions the authenticated user is permitted to access). While it offers standard login and access control by default, real-world applications often require customization, such as:
- Fetching user details from a database or external system.
- Handling multiple roles and permissions.
- Providing custom login and registration mechanisms.
- Integrating OAuth, JWT, or other advanced security protocols.
Authentication
Authentication verifies the identity of a user, typically done by checking the username and password in Spring Security. The framework supports multiple authentication methods, including in-memory, database, LDAP, OAuth, etc.
Key Components of Authentication:
UserDetailsService
: Retrieves user-related data and has the method loadUserByUsername()
to locate users by their username.AuthenticationManager
: Manages authentication and delegates the logic to AuthenticationProvider
.AuthenticationProvider
: Responsible for performing authentication by verifying user credentials.PasswordEncoder
: Handles password encryption. The common implementation is BCryptPasswordEncoder
, which provides hashing for secure password storage.
Authorization
Authorization determines access to resources based on roles or permissions after authentication. Once authenticated, a user's access is controlled based on assigned roles (e.g., ROLE_USER
, ROLE_ADMIN
).
Common Types of Authorization:
- Role-based authorization: Permissions are assigned based on roles.
- Method-level authorization: Restricts access to specific methods or endpoints based on roles or custom access policies.
Spring Security authorization mechanism includes:
- Access Control via URL Patterns: By defining the security rules in the configuration class (HttpSecurity), we can control which URL paths require the certain roles or privileges.
- Role Hierarchy: Spring allows us to define the hierarchy for the roles (e.g., ROLE_ADMIN can access everything that ROLE_USER can).
Key Components of Authorization:
HttpSecurity
: Defines URL access rules and what roles or permissions are needed.@PreAuthorize
and @Secured
Annotations: Used for method-level access control.AccessDecisionManager
: Makes final authorization decisions after evaluating user roles.
Customizing Authentication and Authorization in Spring Security
Step 1: Create a New Spring Boot Project
In IntelliJ IDEA, create a new Spring Boot project with the following options:
- Name:
spring-security-custom-auth
- Language: Java
- Type: Maven
- Packaging: Jar
Click on the Next button.
Step 2: Add Dependencies
Add the following dependencies in pom.xml
:
- Spring Web
- Spring Security
- Lombok
- Spring DevTools
- Spring Data JPA
- MySQL Driver
Click on the Create button.
Project Structure
After project creation done, the folder structure will look like the below image:
Step 3: Configure Application Properties
Add the following MySQL and Hibernate configuration in application.properties
:
# Application name
spring.application.name=spring-security-custom-auth
# MySQL Database Configuration
spring.datasource.url=jdbc:mysql://localhost:3306/exampledb?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=mypassword
# Hibernate JPA Configuration
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.show-sql=true
Step 4: Create the User Class
Java
package com.gfg.springsecuritycustomauth.model;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Set;
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // Auto-generates the ID for the user
private Long id;
private String username; // Stores the username of the user
private String password; // Stores the encrypted password
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "user_roles", // Defines the join table for user and role mapping
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles; // Set of roles associated with the user
}
Step 5: Create the Role Class
Java
package com.gfg.springsecuritycustomauth.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
Step 6: Create the UserRepository Interface
Java
package com.gfg.springsecuritycustomauth.repository;
import com.gfg.springsecuritycustomauth.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
}
Step 7: Create the RoleRepository Interface
Java
package com.gfg.springsecuritycustomauth.repository;
import com.gfg.springsecuritycustomauth.model.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
}
Step 8: Create the SecurityConfig Class
Java
package com.gfg.springsecuritycustomauth.config;
import com.gfg.springsecuritycustomauth.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private CustomUserDetailsService userDetailsService;
// BCryptPasswordEncoder Bean to be used in password encoding
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// Define the security filter chain for handling HTTP security rules
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// Define which requests require authentication and authorization
http
.csrf().disable() // CSRF protection is disabled for simplicity, reconsider enabling it in production
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/admin/**").hasRole("ADMIN") // Only ADMIN role can access /admin/*
.requestMatchers("/user/**").hasRole("USER") // Only USER role can access /user/*
.requestMatchers("/public/**").permitAll() // Public endpoints that don't require authentication
.anyRequest().authenticated() // All other requests require authentication
)
.formLogin(form -> form
.loginPage("") // Custom login page
.permitAll() // Allow anyone to access the login page
)
.logout(logout -> logout
.permitAll() // Allow logout without restriction
);
return http.build(); // Return the configured SecurityFilterChain
}
// AuthenticationManager for custom user details service and password encoding
@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authManagerBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
// Configure custom UserDetailsService and password encoder
authManagerBuilder
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
return authManagerBuilder.build(); // Build and return the AuthenticationManager
}
}
Step 9: CustomUserDetailsService Class
Java
package com.gfg.springsecuritycustomauth.service;
import com.gfg.springsecuritycustomauth.model.User;
import com.gfg.springsecuritycustomauth.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.stream.Collectors;
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return new org.springframework.security.core.userdetails.User(user.getUsername(),
user.getPassword(),
getAuthorities(user));
}
private Collection<? extends GrantedAuthority> getAuthorities(User user) {
return user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toList());
}
}
Step 10: Create a DataSeeder Class
Java
package com.gfg.springsecuritycustomauth;
import com.gfg.springsecuritycustomauth.model.Role;
import com.gfg.springsecuritycustomauth.model.User;
import com.gfg.springsecuritycustomauth.repository.RoleRepository;
import com.gfg.springsecuritycustomauth.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import java.util.Set;
@Component
public class DataSeeder implements CommandLineRunner {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public void run(String... args) throws Exception {
Role userRole = new Role();
userRole.setName("ROLE_USER");
roleRepository.save(userRole);
Role adminRole = new Role();
adminRole.setName("ROLE_ADMIN");
roleRepository.save(adminRole);
User user = new User();
user.setUsername("user");
user.setPassword(passwordEncoder.encode("userpassword"));
user.setRoles(Set.of(userRole));
userRepository.save(user);
User admin = new User();
admin.setUsername("admin");
admin.setPassword(passwordEncoder.encode("adminpassword"));
admin.setRoles(Set.of(adminRole));
userRepository.save(admin);
}
}
Step 11: Create the UserController Class
Java
package com.gfg.springsecuritycustomauth.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class UserController {
// Public endpoint, accessible to anyone
@GetMapping("/public")
public String publicAccess() {
return "This is a public endpoint accessible to anyone.";
}
// Endpoint for authenticated users with ROLE_USER
@GetMapping("/user/dashboard")
@PreAuthorize("hasRole('USER')")
public String userDashboard() {
return "Welcome to the user dashboard!";
}
// Endpoint for authenticated users with ROLE_ADMIN
@GetMapping("/admin/dashboard")
@PreAuthorize("hasRole('ADMIN')")
public String adminDashboard() {
return "Welcome to the admin dashboard!";
}
}
Step 12: Main Class
Java
package com.gfg.springsecuritycustomauth;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringSecurityCustomAuthApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSecurityCustomAuthApplication.class, args);
}
}
pom.xml
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gfg</groupId>
<artifactId>spring-security-custom-auth</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-security-custom-auth</name>
<description>spring-security-custom-auth</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Step 13: Run the Application
After all the steps done, now run the project, and it will run on the port number 8080.
Step 14: Testing the Application
We will now test the application using the Postman tool.
1. Public Endpoint
http://localhost:8080/api/public
Output:
2. Admin Dashboard
http://localhost:8080/api/admin/dashboard
Output:
3. User Dashboard
GET http://localhost:8080/api/user/dashboard
Output:
Conclusion
In this article, we've explored how to customize authentication and authorization in a Spring Boot application using Spring Security. We've created a basic configuration that allows for role-based access control and defined user roles and permissions.
Similar Reads
Implementing Database Authentication and Authorization with Spring Security 6
Spring Security 6 has made setting up security in Spring applications easier and more straightforward by moving away from the older WebSecurityConfigurerAdapter method. This new approach helps you configure security in a clearer and more flexible way. In this article, we will learn how to set up use
9 min read
Spring Boot - OAuth2 Authentication and Authorization
OAuth2 is a widely-used protocol for authorization that enables applications to securely access resources on behalf of users. When combined with Spring Boot, OAuth2 facilitates authentication and authorization for both REST APIs and web applications. This article will walk you through setting up OAu
7 min read
Spring Security Custom AuthenticationFailureHandler
In Java, Spring Security is a very powerful framework that can provide comprehensive security services for Java enterprise software applications. One of the essential aspects of the security is authentication and it can be users are verified before granting access to the resource. Spring Security ca
6 min read
Authentication and Authorization in Microservices
In microservices, ensuring data security is paramount. Authentication and authorization are two crucial components of this security framework. This article provides a straightforward overview of how authentication verifies user identity and how authorization controls access to resources within micro
11 min read
API Gateway Authentication and Authorization in Spring Boot
In modern web applications, securing the communication between the clients and backend services is crucial. The API Gateway can serves as the reverse proxy and managing the client requests, and routing them to the appropriate the backend services. It enhance the security, we can implement the authen
12 min read
Authentication and Authorization with OAuth
OAuth (Open Authorization) is the open standard for token-based authentication and authorization on the Internet. It can allow third-party services to exchange information without exposing the user credentials. In this article, we will guide you on how to implement the OAuth in the MERN stack applic
7 min read
Authentication and Authorization with React Hooks
Authentication and Authorization are fundamental aspects of web development, ensuring that users have secure access to resources within an application. With the introduction of React Hooks, managing authentication and authorization in React applications has become more streamlined and efficient. In
3 min read
Spring Security - Basic Authentication
Spring Security is a framework that allows a programmer to use JEE (Java Enterprise Edition) components to set security limitations on Spring Framework-based web applications. As a core part of the Spring ecosystem, itâs a library that can be utilized and customized to suit the demands of the progra
7 min read
Authentication in Spring Security
In Spring Security, âauthenticationâ is the process of confirming that a user is who they say they are and that they have the right credentials to log in to a protected resource or to perform a privileged action in an application. Spring Security helps you set up different authentication methods, li
13 min read
Spring Security - In-Memory Authentication
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications. Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring
4 min read