Why Spring Security Is Hard to Learn Spring Security is not complex because the concepts are complex. Authentication and authorization are straightforward ideas. Spring Security is hard because it hides its machinery: a request comes in, something happens, an endpoint is secured or not — and without knowing the internal architecture, the behaviour feels magical and the configuration feels arbitrary. This article removes the magic. By the end, you will have the mental model needed to understand every other concept in this series.
Continue reading »Spring-Boot
209 posts in this section
HTTP Authorization: Securing Endpoints with requestMatchers
HTTP Authorization: The Last Line of Defense AuthorizationFilter is the last filter in the Spring Security chain. After all authentication filters have run (and either set an Authentication or left it as anonymous), AuthorizationFilter makes the final access decision: allow or deny. The rules you write in authorizeHttpRequests() are evaluated in order, first match wins. flowchart TD Request[HTTP Request] --> Auth[AuthorizationFilter] Auth --> Rules{Evaluate rules\nin order} Rules -->|Rule 1 matches| Decision1{Allowed?
Continue reading »HTTP Basic Authentication and Stateless APIs
What Is HTTP Basic Authentication? HTTP Basic is the simplest authentication scheme defined in the HTTP specification (RFC 7617). The client encodes username:password in Base64 and sends it in the Authorization header with every request: Authorization: Basic YWxpY2U6c2VjcmV0 ↑ Base64("alice:secret") Important: Base64 is encoding, not encryption. Anyone who intercepts the request can decode the credentials instantly. HTTP Basic must only be used over HTTPS. sequenceDiagram participant Client as API Client participant SSF as Spring Security Filters participant AM as AuthenticationManager Client->>SSF: GET /api/data\n(no Authorization header) SSF-->>Client: 401 Unauthorized\nWWW-Authenticate: Basic realm="
Continue reading »Idempotent Producers: Eliminating Duplicate Messages
The Duplicate Problem With acks=all and retries enabled, a produce request might be acknowledged by the broker, but the acknowledgment is lost in the network before reaching the producer. The producer, seeing no response, retries — sending the same record again. The broker writes it a second time. The consumer sees a duplicate. sequenceDiagram participant Producer participant Leader as Broker (Leader) Producer->>Leader: ProduceRequest: OrderPlaced (orderId=1001) Leader->>Leader: Write record at offset 42 ✓ Leader--xProducer: ProduceResponse LOST (network failure) Note over Producer: No ack received — retrying Producer->>Leader: ProduceRequest: OrderPlaced (orderId=1001) [RETRY] Leader->>Leader: Write record at offset 43 ✓ (DUPLICATE!
Continue reading »Inheritance Strategies: SINGLE_TABLE, JOINED, TABLE_PER_CLASS
Introduction Object-oriented code uses inheritance to share behaviour. Relational databases have no concept of inheritance. JPA bridges this gap with three strategies for mapping a class hierarchy to tables. Understanding when each is appropriate prevents schema headaches and performance problems. The Domain Example An e-commerce system has different types of discount: Discount (abstract) ├── PercentageDiscount (e.g., 10% off) └── FixedAmountDiscount (e.g., $5 off) All discounts share: id, name, validFrom, validUntil. PercentageDiscount adds: percentage (e.
Continue reading »Introduction to JPA, Hibernate, and Spring Data JPA
Introduction Every Spring Boot application that touches a relational database eventually encounters three terms used almost interchangeably: JPA, Hibernate, and Spring Data JPA. They are related but distinct, and understanding the difference is essential before writing a single line of mapping code. This article explains what each one is, how they fit together, and why this stack is the dominant approach to Java database access. The Problem: Object-Relational Impedance Mismatch Java applications work with objects: classes, inheritance, collections, references.
Continue reading »JSON Serialization: JsonSerializer, JsonDeserializer, and Type Mapping
The Serialization Problem Kafka stores bytes. KafkaTemplate<String, OrderPlacedEvent> needs to turn your Java object into bytes for the producer, and @KafkaListener needs to turn those bytes back into the right Java class on the consumer. Spring Kafka ships JsonSerializer and JsonDeserializer built on Jackson to handle this — but they have several sharp edges that break in real multi-service deployments. How Spring Kafka JSON Serialization Works flowchart LR subgraph Producer["Order Service"
Continue reading »JWT Authentication: Stateless Token-Based Security
What Is JWT? JWT (JSON Web Token, RFC 7519) is a compact, URL-safe token format for representing claims between two parties. It is the standard for stateless REST API authentication — the server issues a signed token at login, and the client presents it on every subsequent request. No session, no cookie, no server-side state. JWT Structure A JWT has three Base64URL-encoded parts separated by dots: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbGljZSIsInJvbGVzIjpbIlJPTEVfVVNFUiJdfQ.signature Header Payload Signature block-beta columns 3 A["
Continue reading »Kafka Architecture: Brokers, Topics, Partitions, and Replicas
The Cluster: Brokers and the Controller A Kafka cluster is a group of servers, each called a broker. Brokers store data and serve producer/consumer requests. One broker in the cluster acts as the controller — it manages partition leadership, handles broker joins and departures, and coordinates rebalancing. In KRaft mode (Kafka 3.3+, the default from Kafka 4.0), the controller is built into Kafka itself — no ZooKeeper needed. flowchart TB subgraph Cluster["
Continue reading »Kafka Consumer in Spring Boot: @KafkaListener Basics
How @KafkaListener Works @KafkaListener is a Spring Kafka annotation that registers a method as a Kafka consumer. Under the hood, Spring Kafka creates a ConcurrentMessageListenerContainer — a managed thread pool that continuously polls the broker and dispatches records to your method. flowchart LR Broker["Kafka Broker"] subgraph Container["ConcurrentMessageListenerContainer"] T1["Poll Thread 1\n(Partition 0)"] T2["Poll Thread 2\n(Partition 1)"] T3["Poll Thread 3\n(Partition 2)"] end Method["@KafkaListener\nvoid onOrderPlaced(...)"] Broker -->|"fetch records"| T1 Broker -->|"fetch records"| T2 Broker -->|"
Continue reading »