Home Blog Page 87

Creating Custom Spring Boot Starters

0
java spring boot course
java spring boot course

Table of Contents

  1. Introduction to Spring Boot Starters
  2. Why Create a Custom Starter?
  3. Anatomy of a Spring Boot Starter
  4. Creating the Starter Module
  5. Defining Auto-Configuration
  6. Using @Conditional Annotations
  7. Registering the Auto-Configuration Class
  8. Publishing Your Starter
  9. Using Your Starter in a Project
  10. Best Practices
  11. Summary

1. Introduction to Spring Boot Starters

Spring Boot starters are dependency descriptors that bundle together related libraries, configurations, and auto-configuration logic. For instance, when you add spring-boot-starter-web, it pulls in all dependencies needed to build a web application and configures them automatically.

A custom Spring Boot starter allows you to encapsulate common functionality (libraries, configurations, and auto-configs) and share it across multiple Spring Boot projects.


2. Why Create a Custom Starter?

Creating a custom starter is useful when:

  • You want to standardize configuration across microservices.
  • You repeatedly use the same set of dependencies and configuration classes.
  • You are developing an internal or third-party SDK with Spring Boot support.

3. Anatomy of a Spring Boot Starter

A typical custom starter project has two Maven modules:

  1. Autoconfigure Module – Contains the configuration logic.
  2. Starter Module – Brings in the autoconfigure module and defines the starter dependency.

4. Creating the Starter Module

Step 1: Set Up Two Maven Modules

  • my-custom-starter-autoconfigure
  • my-custom-starter

Directory structure:

my-custom-starter/
├── my-custom-starter/
│ └── pom.xml
├── my-custom-starter-autoconfigure/
│ └── pom.xml
└── pom.xml

5. Defining Auto-Configuration

In the my-custom-starter-autoconfigure module:

Add Required Dependencies

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

Create a Configuration Class

@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyServiceAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public MyService myService(MyServiceProperties properties) {
return new MyService(properties.getMessage());
}
}

Properties Class

@ConfigurationProperties("my.service")
public class MyServiceProperties {
private String message = "Hello from MyService";

public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}

6. Using @Conditional Annotations

Spring Boot’s conditional annotations make autoconfiguration smart:

  • @ConditionalOnClass: Only configure if class is present.
  • @ConditionalOnMissingBean: Only create bean if it’s not already defined.
  • @ConditionalOnProperty: Enable config based on property value.

Example:

@Bean
@ConditionalOnProperty(name = "my.service.enabled", havingValue = "true", matchIfMissing = true)
public MyService myService() {
return new MyService();
}

7. Registering the Auto-Configuration Class

You must register the auto-configuration class using the spring.factories file.

In:
src/main/resources/META-INF/spring.factories

Add:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfig.MyServiceAutoConfiguration

8. Publishing Your Starter

Package both modules and publish to your internal or public Maven repository.

mvn clean install
# or publish to remote repo

9. Using Your Starter in a Project

Now you can add your starter in another project:

<dependency>
<groupId>com.example</groupId>
<artifactId>my-custom-starter</artifactId>
<version>1.0.0</version>
</dependency>

And optionally configure it in application.properties:

my.service.enabled=true
my.service.message=Hello from Custom Starter

10. Best Practices

  • Keep logic in the autoconfigure module; starter should be a thin wrapper.
  • Use appropriate @Conditional annotations to avoid conflicts.
  • Provide reasonable defaults in your properties classes.
  • Document available properties for users of your starter.

11. Summary

Creating a custom Spring Boot starter streamlines repetitive setup and promotes consistent configurations across your projects. By encapsulating dependencies, auto-configurations, and custom properties, you make your reusable functionality portable and production-ready.

Integration Testing with Spring Boot

0
java spring boot course
java spring boot course

Table of Contents

  1. Introduction to Integration Testing
  2. Difference Between Unit and Integration Testing
  3. Setting Up Integration Tests in Spring Boot
  4. Using @SpringBootTest for Full-Context Testing
  5. Testing REST Controllers with TestRestTemplate and MockMvc
  6. Testing Repositories with Embedded Databases
  7. Using Testcontainers for Realistic Integration Tests
  8. Transaction Rollbacks and Test Isolation
  9. Data Initialization for Tests (@Sql, @DataJpaTest, @TestEntityManager)
  10. Best Practices for Integration Testing
  11. Summary

1. Introduction to Integration Testing

Integration testing ensures that different layers or components of your application work together correctly. It verifies the behavior of the application in a more realistic runtime environment, where various components interact.

In Spring Boot, integration tests are used to:

  • Test REST API endpoints from controller to database.
  • Validate Spring configurations and beans.
  • Simulate user behavior end-to-end.

2. Difference Between Unit and Integration Testing

FeatureUnit TestingIntegration Testing
ScopeTests single class or methodTests multiple components
SpeedVery fastSlower due to more setup
DependenciesTypically mockedUse real components
FrameworksJUnit + MockitoJUnit + Spring Boot Test, Testcontainers, etc.

3. Setting Up Integration Tests in Spring Boot

Dependencies in Maven

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

<!-- For Testcontainers (optional) -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.1</version>
<scope>test</scope>
</dependency>

4. Using @SpringBootTest for Full-Context Testing

The @SpringBootTest annotation loads the full application context.

@SpringBootTest
class UserServiceIntegrationTest {
@Autowired
private UserService userService;

@Test
void testCreateUser() {
User user = userService.create("Alice");
assertNotNull(user.getId());
}
}

This test will initialize all Spring components, configurations, and data sources.


5. Testing REST Controllers with TestRestTemplate and MockMvc

With TestRestTemplate

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserControllerTest {
@Autowired
private TestRestTemplate restTemplate;

@Test
void testGetUser() {
ResponseEntity<User> response = restTemplate.getForEntity("/users/1", User.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
}

With MockMvc

@AutoConfigureMockMvc
@SpringBootTest
class UserControllerMockMvcTest {
@Autowired
private MockMvc mockMvc;

@Test
void testGetUser() throws Exception {
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("Alice"));
}
}

6. Testing Repositories with Embedded Databases

You can use @DataJpaTest to test repositories with an in-memory database (like H2):

@DataJpaTest
class UserRepositoryTest {
@Autowired
private UserRepository repository;

@Test
void testFindById() {
User saved = repository.save(new User("Alice"));
Optional<User> found = repository.findById(saved.getId());
assertTrue(found.isPresent());
}
}

7. Using Testcontainers for Realistic Integration Tests

Instead of using H2, Testcontainers allows you to run real database instances (like PostgreSQL) in Docker containers for more production-like tests.

@Testcontainers
@SpringBootTest
class PostgreSQLContainerTest {

@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:14")
.withDatabaseName("testdb")
.withUsername("user")
.withPassword("pass");

@DynamicPropertySource
static void setDatasourceProps(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}

@Test
void testDbInteraction() {
// use the real PostgreSQL instance
}
}

8. Transaction Rollbacks and Test Isolation

By default, Spring tests annotated with @Transactional will roll back transactions after each test:

@Transactional
@Test
void testTransactionalRollback() {
userRepository.save(new User("Bob"));
// data won't persist after the test
}

This keeps tests isolated and database state clean.


9. Data Initialization for Tests

You can preload data using:

@Sql Annotation

@Sql("/test-data.sql")
@Test
void testWithPreloadedData() {
// test logic
}

@TestEntityManager (in @DataJpaTest)

@Autowired
private TestEntityManager entityManager;

@Test
void testUsingEntityManager() {
User user = new User("Carol");
entityManager.persist(user);
}

10. Best Practices for Integration Testing

  • Use @SpringBootTest only when needed. Prefer @WebMvcTest and @DataJpaTest for lighter tests.
  • Reset the database state between tests to prevent data leaks.
  • Use Testcontainers for a more realistic test environment.
  • Keep your test environment configuration (like test DB credentials) separate from production.
  • Avoid complex logic in tests—focus on expected behavior.

11. Summary

Integration testing in Spring Boot ensures your application components work together seamlessly. From REST endpoints to database operations, it validates the actual behavior of your application under realistic conditions. By combining tools like @SpringBootTest, MockMvc, TestRestTemplate, and Testcontainers, you can build a robust and maintainable testing suite for your Spring Boot application.

Unit Testing with JUnit and Mockito in Java

0
java spring boot course
java spring boot course

Table of Contents

  1. Introduction to Unit Testing
  2. Setting Up JUnit and Mockito
  3. Writing Your First Unit Test with JUnit
  4. Understanding Assertions
  5. Introduction to Mockito
  6. Writing Unit Tests Using Mockito
  7. Mocking and Stubbing Dependencies
  8. Argument Matchers and Verification
  9. Best Practices in Unit Testing
  10. Summary

1. Introduction to Unit Testing

Unit testing is the process of testing individual units or components of a software application in isolation to ensure they work as expected. In Java, JUnit is the most popular framework for writing unit tests, and Mockito is the de facto standard for mocking dependencies.

The main goals of unit testing are to:

  • Ensure correctness of individual units (methods/classes)
  • Simplify debugging
  • Improve code quality and reliability
  • Enable refactoring with confidence

2. Setting Up JUnit and Mockito

Using Maven

Add the following dependencies to your pom.xml:

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>

Using Gradle

testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1'
testImplementation 'org.mockito:mockito-core:5.2.0'

Also, ensure JUnit 5 is enabled with:

test {
useJUnitPlatform()
}

3. Writing Your First Unit Test with JUnit

Create a simple class:

public class Calculator {
public int add(int a, int b) {
return a + b;
}
}

Test it using JUnit:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class CalculatorTest {
@Test
void testAddition() {
Calculator calc = new Calculator();
assertEquals(5, calc.add(2, 3));
}
}

4. Understanding Assertions

JUnit provides a variety of assertion methods:

  • assertEquals(expected, actual)
  • assertNotEquals(expected, actual)
  • assertTrue(condition)
  • assertFalse(condition)
  • assertThrows(exception.class, executable)
  • assertAll() — to group multiple assertions

Example:

assertAll(
() -> assertEquals(4, calc.add(2, 2)),
() -> assertNotEquals(5, calc.add(2, 2))
);

5. Introduction to Mockito

Mockito allows you to create mock objects for dependencies. It’s useful when testing service classes that rely on repositories or external systems.

import static org.mockito.Mockito.*;

MyRepository mockRepo = mock(MyRepository.class);

6. Writing Unit Tests Using Mockito

Let’s say we have a service class:

public class UserService {
private final UserRepository userRepository;

public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}

public User findUser(String id) {
return userRepository.findById(id);
}
}

Test with Mockito:

import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

class UserServiceTest {
@Test
void testFindUser() {
UserRepository mockRepo = mock(UserRepository.class);
User user = new User("1", "Alice");
when(mockRepo.findById("1")).thenReturn(user);

UserService service = new UserService(mockRepo);
User result = service.findUser("1");

assertEquals("Alice", result.getName());
verify(mockRepo).findById("1");
}
}

7. Mocking and Stubbing Dependencies

Mocking allows you to:

  • Replace actual implementations with controlled behavior
  • Simulate exceptions
  • Return dummy values for tests

Example:

when(repository.save(any(User.class))).thenThrow(new RuntimeException("DB Error"));

8. Argument Matchers and Verification

Mockito provides matchers like any(), eq(), contains(), etc.

verify(repository, times(1)).findById(eq("1"));

You can also capture arguments with ArgumentCaptor.

ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
verify(repository).save(captor.capture());
assertEquals("Bob", captor.getValue().getName());

9. Best Practices in Unit Testing

  • Test one unit of functionality per test case
  • Keep test methods short and focused
  • Use meaningful test method names
  • Don’t overuse mocks—test real behavior where possible
  • Write both positive and negative test cases
  • Ensure tests are independent and deterministic

10. Summary

Unit testing with JUnit and Mockito enables you to build reliable and maintainable Java applications. You can test classes in isolation, mock dependencies, and simulate various scenarios without relying on external systems.

CORS, CSRF, and Secure Headers in Spring Boot

0
java spring boot course
java spring boot course

Table of Contents

  1. Introduction
  2. What is CORS?
    • Why CORS Exists
    • Configuring CORS in Spring Boot
  3. What is CSRF?
    • CSRF Explained
    • CSRF in State vs Stateless Applications
    • Disabling or Enabling CSRF in Spring Boot
  4. Secure HTTP Headers
    • Why Headers Matter
    • Using Spring Security Headers
  5. Best Practices for API Security
  6. Summary

1. Introduction

When securing a Spring Boot application—especially one exposed over HTTP or HTTPS—it’s crucial to handle CORS, CSRF, and security headers properly. These are often overlooked but form the first line of defense against attacks like cross-site scripting (XSS), cross-site request forgery, clickjacking, and more.


2. What is CORS? (Cross-Origin Resource Sharing)

CORS is a browser mechanism that blocks requests from one origin to another unless explicitly allowed by the server.

Why CORS Exists

Say your frontend (React, Angular) is running on http://localhost:3000 and your backend is on http://localhost:8080. This is considered cross-origin. Browsers block such requests unless the backend says, “I trust this origin.”

Configuring CORS in Spring Boot

There are multiple ways to enable CORS:

At Controller Level

@CrossOrigin(origins = "http://localhost:3000")
@RestController
public class MyController {
// endpoints
}

Globally via WebMvcConfigurer

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true);
}
}

With Spring Security

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.cors(Customizer.withDefaults());
return http.build();
}

And define a CorsConfigurationSource bean if you need fine control.


3. What is CSRF? (Cross-Site Request Forgery)

CSRF Explained

CSRF is an attack where a malicious site tricks a user’s browser into sending unwanted requests to your site, exploiting authenticated sessions.

CSRF in State vs Stateless Applications

  • Stateful Apps (with Sessions): CSRF is a real threat.
  • Stateless REST APIs: Usually CSRF protection is disabled because JWT tokens or other mechanisms are used, and no session is maintained.

Disabling CSRF in Spring Boot

http.csrf().disable();

This is typical in JWT-based authentication scenarios.

Enabling CSRF (If Needed)

For form-based login:

http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

This stores a CSRF token in a cookie and expects it in a header with every request.


4. Secure HTTP Headers

Headers can protect your app from various attacks:

Common Secure Headers

HeaderPurpose
X-Content-Type-OptionsPrevents MIME type sniffing
X-Frame-OptionsPrevents clickjacking
Strict-Transport-SecurityForces HTTPS
X-XSS-ProtectionEnables basic XSS protection
Content-Security-PolicyRestricts sources for scripts, etc.
Referrer-PolicyControls referrer information

Enable Headers in Spring Security

http.headers()
.contentTypeOptions()
.and()
.frameOptions().deny()
.and()
.httpStrictTransportSecurity().includeSubDomains(true).maxAgeInSeconds(31536000)
.and()
.xssProtection().block(true);

For CSP (Content Security Policy):

http.headers()
.contentSecurityPolicy("script-src 'self'; object-src 'none';");

5. Best Practices for API Security

  • Always validate input on both client and server side.
  • Enable CORS only for trusted origins.
  • Disable CSRF if using stateless tokens (JWT, OAuth2), else enable it.
  • Enforce HTTPS and HSTS.
  • Apply security headers for browsers.
  • Rate limit and throttle API requests.
  • Log and monitor for unusual activity.

6. Summary

Security features like CORS, CSRF, and secure HTTP headers may seem minor, but they’re essential to protect your app from common web threats.

  • CORS allows cross-origin requests securely.
  • CSRF should be disabled in token-based APIs but enabled for form-based sessions.
  • Security headers add layers of protection against XSS, clickjacking, and protocol downgrade attacks.

Role-Based Access Control (RBAC) in Spring Security

0
java spring boot course
java spring boot course

Table of Contents

  1. What is RBAC?
  2. Benefits of Using RBAC
  3. Defining Roles in Spring Security
  4. Role-Based Access with Annotations
  5. Role Hierarchy in Spring Security
  6. Fine-Grained Access Control Using Expressions
  7. Best Practices for Implementing RBAC
  8. Summary

1. What is RBAC?

Role-Based Access Control (RBAC) is a security mechanism that restricts access to resources based on the roles assigned to users. In Spring Security, roles determine what actions a user can perform or what endpoints they can access.

Example:

  • Role ADMIN may access /admin/**
  • Role USER may only access /user/**

2. Benefits of Using RBAC

  • Centralized control: Easier management of access rules.
  • Scalability: Adding new roles or users doesn’t require changing application logic.
  • Improved security: Reduces chances of unauthorized access.
  • Auditable: Easier to audit and enforce organizational security policies.

3. Defining Roles in Spring Security

Step 1: User Definition with Roles

@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withUsername("user")
.password(passwordEncoder().encode("user123"))
.roles("USER")
.build();

UserDetails admin = User.withUsername("admin")
.password(passwordEncoder().encode("admin123"))
.roles("ADMIN")
.build();

return new InMemoryUserDetailsManager(user, admin);
}

Spring automatically prefixes ROLE_ to each role. So roles("ADMIN") becomes ROLE_ADMIN.


4. Role-Based Access with Annotations

@PreAuthorize and @Secured

Using @Secured:

@Secured("ROLE_ADMIN")
@GetMapping("/admin/dashboard")
public String adminDashboard() {
return "Welcome Admin!";
}

Using @PreAuthorize:

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

To enable annotations:

@EnableMethodSecurity(securedEnabled = true, prePostEnabled = true)
@Configuration
public class SecurityConfig {
// your security beans
}

5. Role Hierarchy in Spring Security

You can define role inheritance so that a higher role includes the authorities of lower roles.

@Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
hierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
return hierarchy;
}

This means an ADMIN implicitly has all permissions of a USER.


6. Fine-Grained Access Control Using Expressions

Spring Security provides rich expression support with @PreAuthorize and @PostAuthorize.

Examples:

@PreAuthorize("hasRole('ADMIN') or hasRole('MODERATOR')")
public String accessControl() {
return "Admin or Moderator Access";
}

@PreAuthorize("#user.username == authentication.name")
public String getUserData(User user) {
return "Accessing own data";
}

You can access:

  • authentication: the current authentication object
  • principal: the current logged-in user
  • #param: method parameters

7. Best Practices for Implementing RBAC

  • Use meaningful role names (ROLE_VIEWER, ROLE_MANAGER) over generic ones.
  • Externalize role definitions if possible (e.g., in DB or LDAP).
  • Use role hierarchy to simplify rules.
  • Apply the principle of least privilege—grant only necessary permissions.
  • Regularly audit roles and access mappings.

8. Summary

RBAC is a powerful and scalable way to manage access control in Spring applications. It lets you separate who can access what, based on roles rather than hard-coded user IDs.

Key Concepts Covered:

  • Defining users with roles
  • Using @Secured, @PreAuthorize for enforcement
  • Configuring role hierarchies
  • Using security expressions for dynamic control

Next, we’ll explore more advanced topics like JWT-based authentication, OAuth2, and custom authorization filters for building highly secure RESTful applications.