Home Blog Page 81

JWT-Based Authentication (Stateless Auth) in Spring Boot

0
java spring boot course
java spring boot course

Table of Contents

  1. Introduction to JWT
  2. How JWT Works (Stateless Auth)
  3. Benefits of JWT in Spring Applications
  4. Generating JWT Tokens
  5. Validating JWT Tokens
  6. Creating a Custom Filter
  7. Configuring Spring Security for JWT
  8. Testing the Full Flow
  9. Best Practices
  10. Summary

1. Introduction to JWT

JSON Web Tokens (JWT) are a compact, URL-safe means of representing claims to be transferred between two parties. In the context of Spring Security, JWTs are used to authorize users in a stateless manner—no server-side session storage is required.

A JWT typically contains three parts:

  • Header: Type and signing algorithm.
  • Payload: User information and claims.
  • Signature: Verifies the token wasn’t tampered with.

2. How JWT Works (Stateless Auth)

In JWT-based stateless authentication:

  1. The user logs in with credentials.
  2. Server authenticates and returns a signed JWT token.
  3. The client stores the JWT (usually in localStorage or cookies).
  4. For each request, the client sends the token in the Authorization header.
  5. The server verifies the token and processes the request.

3. Benefits of JWT in Spring Applications

  • Stateless: No session storage on server.
  • Scalable: Ideal for distributed systems.
  • Self-contained: Carries user data and metadata.
  • Cross-platform: Can be used across languages and services.

4. Generating JWT Tokens

Use a utility class to generate tokens:

public class JwtUtil {
private final String SECRET_KEY = "your-secret";

public String generateToken(UserDetails userDetails) {
return Jwts.builder()
.setSubject(userDetails.getUsername())
.claim("roles", userDetails.getAuthorities())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 hrs
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
}

5. Validating JWT Tokens

public boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}

You’ll also need methods like extractUsername(), extractExpiration() etc., using Jwts.parser().


6. Creating a Custom JWT Filter

This filter intercepts requests, extracts the JWT, and sets the security context.

public class JwtRequestFilter extends OncePerRequestFilter {

@Autowired
private JwtUtil jwtUtil;

@Autowired
private UserDetailsService userDetailsService;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {

final String authHeader = request.getHeader("Authorization");

String username = null;
String jwt = null;

if (authHeader != null && authHeader.startsWith("Bearer ")) {
jwt = authHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}

if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

SecurityContextHolder.getContext().setAuthentication(token);
}
}

chain.doFilter(request, response);
}
}

7. Configuring Spring Security for JWT

Update your security configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Autowired
private JwtRequestFilter jwtRequestFilter;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests()
.requestMatchers("/authenticate").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);

http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

return http.build();
}
}

8. Testing the Full Flow

  1. Authenticate:
    • POST to /authenticate with username and password.
    • Receive JWT token.
  2. Access Protected Resource:
    • Set header: Authorization: Bearer <token>
    • GET /api/protected – success if token is valid.

9. Best Practices

  • Always sign tokens with a strong secret or RSA keys.
  • Use HTTPS to secure tokens in transit.
  • Set short expiration times and refresh tokens.
  • Avoid storing JWTs in localStorage on browsers prone to XSS.
  • Use claim fields wisely—don’t overstuff JWTs.

10. Summary

JWT-based authentication provides a scalable, stateless solution for securing your Spring Boot APIs. It avoids session storage, making it ideal for RESTful microservices.

Key Concepts Covered:

  • What JWT is and how it works
  • How to generate, sign, and validate tokens
  • Creating a custom JWT filter
  • Securing endpoints using JWT

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.