In this tutorial, we will learn how to secure REST APIs with Spring Boot 3 and Spring Security 6 using Database Authentication.
Prerequisites
- JDK 17 or later
- Maven
- Spring Boot 3.2+
- An IDE (IntelliJ IDEA, Eclipse, VS Code, etc.)
- A relational database (H2, MySQL, PostgreSQL, etc.)
Step 1: Set Up the Spring Boot Project
1.1 Generate the Project
Use Spring Initializr to generate a new Spring Boot project with the following configuration:
- Project: Maven Project
- Language: Java
- Spring Boot: 3.2.x
- Dependencies: Spring Web, Spring Security, Spring Data JPA, H2 Database
1.2 Download and Open the Project
Download the generated project, unzip it, and open it in your IDE.
Example Project Structure
my-spring-security-app/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/security/
│ │ │ └── SecurityApplication.java
│ │ │ └── config/
│ │ │ └── SecurityConfig.java
│ │ │ └── controller/
│ │ │ └── UserController.java
│ │ │ └── model/
│ │ │ └── User.java
│ │ │ └── repository/
│ │ │ └── UserRepository.java
│ │ │ └── service/
│ │ │ └── UserService.java
│ │ └── resources/
│ │ ├── application.properties
│ └── test/
│ └── java/
│ └── com/example/security/
│ └── SecurityApplicationTests.java
├── mvnw
├── mvnw.cmd
├── pom.xml
└── .mvn/
└── wrapper/
└── maven-wrapper.properties
Step 2: Configure pom.xml
Ensure your pom.xml
file includes the necessary dependencies:
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>security</name>
<description>Demo project for Spring Security</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>17</java.version>
</properties>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Step 3: Configure the Database
3.1 application.properties
Configure the H2 database in the src/main/resources/application.properties
file.
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
# JPA properties
spring.jpa.hibernate.ddl-auto=update
Step 4: Create the User Entity
Create a User
entity in the src/main/java/com/example/security/model
directory.
package com.example.security.model;
import jakarta.persistence.*;
import java.util.Set;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "roles", joinColumns = @JoinColumn(name = "user_id"))
@Column(name = "role")
private Set<String> roles;
// Getters and setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<String> getRoles() {
return roles;
}
public void setRoles(Set<String> roles) {
this.roles = roles;
}
}
Step 5: Create the User Repository
Create a repository interface named UserRepository
in the src/main/java/com/example/security/repository
directory.
package com.example.security.repository;
import com.example.security.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
}
Step 6: Create the User Service
Create a service class named UserService
in the src/main/java/com/example/security/service
directory.
package com.example.security.service;
import com.example.security.model.User;
import com.example.security.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
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.stream.Collectors;
@Service
public class UserService 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: " + username));
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
user.getRoles().stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList())
);
}
}
Step 7: Configure Spring Security
Create a configuration class named SecurityConfig
in the src/main/java/com/example/security/config
directory.
package com.example.security.config;
import com.example.security.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Autowired
private UserService userService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic();
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
return userService;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Step 8: Create the User Controller
Create a REST controller class named UserController
in the src/main/java/com/example/security/controller
directory.
package com.example.security.controller;
import com.example.security.model.User;
import com.example.security.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping
@PreAuthorize("hasRole('ADMIN')")
public List<User> getAllUsers() {
return userRepository.findAll();
}
@GetMapping("/{id}")
@PreAuthorize("hasRole('ADMIN') or (hasRole('USER') and #id == principal.id)")
public Optional<User> getUserById(@PathVariable Long id) {
return userRepository.findById(id);
}
@PostMapping
@PreAuthorize("hasRole('ADMIN')")
public User createUser(@RequestBody User user) {
return userRepository.save(user);
}
@PutMapping("/{id}")
@PreAuthorize("hasRole('ADMIN') or (hasRole('USER') and #id == principal.id)")
public Optional<User> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
return userRepository.findById(id).map(user -> {
user.setUsername(userDetails.getUsername());
user.setPassword(userDetails.getPassword());
user.setRoles(userDetails.getRoles());
return userRepository.save(user);
});
}
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(@PathVariable Long id) {
userRepository.deleteById(id);
}
}
Step 9: Running the Application
9.1 Add Initial Data
Create an import.sql
file in the src/main/resources
directory to add initial data.
INSERT INTO users (username, password) VALUES ('admin', '$2a$10$DowQj5./o6CHrY1V0sBbIu.JhD2z1hcuyxN.k1DSjqPLyDzDlG6Iy'); -- password: admin
INSERT INTO roles (user_id, role) VALUES (1, 'ROLE_ADMIN');
INSERT INTO users (username, password) VALUES ('user', '$2a$10$DowQj5./o6CHrY1V0sBbIu.JhD2z1hcuyxN.k1DSjqPLyDzDlG6Iy'); -- password: user
INSERT INTO roles (user_id, role) VALUES (2, 'ROLE_USER');
9.2 Run the Application
Run the Spring Boot application using your IDE or the command line:
./mvnw spring-boot:run
Step 10: Verify the Application
Use a tool like Postman or curl to test the endpoints with different user roles.
-
Get All Users (Admin only):
- URL:
http://localhost:8080/users
- Method:
GET
- Authentication: Basic Auth with
admin/admin
- URL:
-
Get User by ID (Admin or User):
- URL:
http://localhost:8080/users/1
- Method:
GET
- Authentication: Basic Auth with
admin/admin
oruser/user
- URL:
-
Create User (Admin only):
- URL:
http://localhost:8080/users
- Method:
POST
- Body:
{ "username": "newuser", "password": "password", "roles": ["ROLE_USER"] }
- Authentication: Basic Auth with
admin/admin
- URL:
-
Update User (Admin or User):
- URL:
http://localhost:8080/users/1
- Method:
PUT
- Body:
{ "username": "updateduser", "password": "password", "roles": ["ROLE_USER"] }
- Authentication: Basic Auth with
admin/admin
oruser/user
- URL:
-
Delete User (Admin only):
- URL:
http://localhost:8080/users/1
- Method:
DELETE
- Authentication: Basic Auth with
admin/admin
- URL:
Following these steps, you have successfully secured your Spring Boot application using Spring Security 6 and Basic Authentication with role-based access control.
Comments
Post a Comment