JWT (JSON Web Token) is the standard for stateless REST API authentication. This article builds a complete JWT authentication system — login, token generation, request validation, and token refresh. What is a JWT? A JWT has three base64url-encoded parts separated by dots: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ← Header (algorithm + type) .eyJzdWIiOiJ1c2VyMTIzIiwicm9sZXMiOlsiVVNFUiJdLCJpYXQiOjE3MTQ3MjY0MDAsImV4cCI6MTcxNDczMDAwMH0 ← Payload .SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ← Signature The payload contains claims: { "sub": "user123", // subject (user identifier) "roles": ["USER"], "iat": 1714726400, // issued at "exp": 1714730000 // expires at } The signature is a HMAC of the header+payload — tamper-proof.
Continue reading »Spring Boot Tutorial
59 posts in this section
Logging: SLF4J, Logback, and Structured Logging
Logging done right gives you everything you need to diagnose production issues. Done wrong, it either buries you in noise or leaves you blind. This article covers the full logging stack — from basics to structured production logging. The Logging Stack Your Code → SLF4J API → Logback (implementation) → Appenders (console, file, etc.) SLF4J is the facade — your code always calls LoggerFactory.getLogger() and log.info(). The implementation (Logback) is swappable without changing your code.
Continue reading »Microservices Architecture: When to Split and When Not to
Microservices are not a technology — they’re an organizational strategy. The right reason to split a monolith is team autonomy and independent deployment, not technical elegance. This article covers when splitting makes sense and how to do it without creating a distributed monolith. What Microservices Actually Solve Microservices address two problems: 1. Independent deployment: Team A can deploy the Order Service without coordinating with Team B’s Payment Service. No shared deployment pipeline, no release freeze, no “all-hands” deploy windows.
Continue reading »Migrating from Spring Boot 3.x to 4.0
Migrating from Spring Boot 3.x to 4.0 is straightforward if you’ve kept up with deprecation warnings. This guide walks through every step — from dependency updates to API changes — with before/after examples. Pre-Migration: Fix Boot 3 Deprecations Before bumping the version, fix all deprecation warnings in your current Boot 3.x project. Every deprecated API in Boot 3 is removed in Boot 4. In IntelliJ IDEA: Analyze → Code Cleanup with “Remove deprecated usages” enabled.
Continue reading »Null Safety with JSpecify
NullPointerException is the most common runtime error in Java. JSpecify is the standardized solution — annotate your APIs and get compile-time null safety. Spring Framework 7 adopts JSpecify throughout, and your code can too. The Problem // Is this safe? You don't know without reading the implementation. public Order findById(UUID id) { return repository.findById(id).orElse(null); // returns null! } // The caller has no warning: Order order = service.findById(id); order.cancel(); // ← NullPointerException if not found Without null annotations, every method call is potentially null — you write defensive code everywhere, or you miss a case and get a NPE in production.
Continue reading »OAuth2 Authorization Server with Spring Security
Most teams use a managed auth provider (Keycloak, Auth0). But sometimes you need your own — multi-tenant SaaS, air-gapped environments, or full control over token contents. Spring Authorization Server provides a production-ready OAuth2 + OIDC implementation. When to Build Your Own vs Use a Provider Use a managed provider (Keycloak/Auth0): Most applications. Faster to set up, maintained externally, handles compliance. Build your own: Multi-tenant platforms issuing tokens on behalf of tenant auth providers, air-gapped or regulated environments, products that ARE the identity provider, or when you need full control over token structure and storage.
Continue reading »OAuth2 Resource Server: Validate JWTs from an Auth Provider
In production, you rarely build your own auth server. You use an external provider — Keycloak, Auth0, Okta, or AWS Cognito. This article shows how to configure Spring Boot as a Resource Server that validates JWTs issued by any OIDC-compliant provider. The OAuth2 Architecture ┌─────────────────┐ │ Auth Server │ │ (Keycloak/Auth0) │ │ │ │ Issues JWTs │ │ Publishes JWKS │ └────────┬────────┘ │ ┌────────────┐ │ JWT │ Client │──────────►│ │ (Browser/ │ │ │ Mobile) │ │ └────────────┘ ▼ ┌─────────────────┐ Bearer │ Resource Server │ Token ►│ (Spring Boot) │ │ │ │ Validates JWT │ │ via JWKS URI │ └─────────────────┘ Client authenticates with the Auth Server and receives a JWT Client sends the JWT as Authorization: Bearer <token> to the Resource Server Resource Server validates the JWT by fetching the public key from the Auth Server’s JWKS endpoint If valid, the Resource Server processes the request Setup <dependency> <groupId>org.
Continue reading »Pagination and Sorting in Spring Boot
Returning all records from a large table in a single response is a recipe for slow APIs and crashed servers. Pagination is not optional — this article shows how to implement it properly with Spring Data. The Problem with Returning Everything // Never do this for large datasets @GetMapping("/api/orders") public List<Order> getOrders() { return orderRepository.findAll(); // 1 million orders → OutOfMemoryError } Even for “small” tables, always paginate. Requirements change, data grows.
Continue reading »Password Encoding and User Authentication
Every application needs user registration and login. This article builds a complete authentication system — from storing passwords safely to handling failed login attempts. Never Store Passwords in Plain Text Store a one-way hash, not the password. BCrypt is the industry standard: @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(12); // cost factor 12 → ~250ms per hash on modern hardware // strong enough to slow down brute-force attacks } BCrypt properties:
Continue reading »Producing and Consuming Kafka Messages
This article implements Kafka producers and consumers with the full production setup — error handling, retries, dead-letter topics, and idempotent consumers. Producer: KafkaTemplate @Service @RequiredArgsConstructor @Slf4j public class OrderEventPublisher { private final KafkaTemplate<String, Object> kafkaTemplate; public void publishOrderCreated(Order order) { OrderCreatedEvent event = new OrderCreatedEvent( order.getId(), order.getCustomerId(), order.getCustomerEmail(), order.getItems().stream().map(OrderItemDto::from).toList(), order.getTotalAmount(), Instant.now() ); // Key = customerId: all events for same customer go to same partition kafkaTemplate.send("order-events", order.getCustomerId().toString(), event) .whenComplete((result, ex) -> { if (ex !
Continue reading »