java
49 lines · 9 steps
How HMAC-signed JWTs are created and verified
A compact Java class that signs JWTs with HMAC-SHA256 and verifies them safely against tampering and expiry.
Explained by
highlit
1public final class HmacJwt {
2
3 private static final ObjectMapper MAPPER = new ObjectMapper();
4 private static final Base64.Encoder ENCODER = Base64.getUrlEncoder().withoutPadding();
5 private static final Base64.Decoder DECODER = Base64.getUrlDecoder();
6
7 private final SecretKeySpec key;
8
9 public HmacJwt(String secret) {
10 this.key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
11 }
12
13 public String sign(Map<String, Object> claims) throws Exception {
14 Map<String, Object> header = Map.of("alg", "HS256", "typ", "JWT");
15 String signingInput = encode(header) + "." + encode(claims);
16 String signature = ENCODER.encodeToString(hmac(signingInput));
17 return signingInput + "." + signature;
18 }
19
20 public Map<String, Object> verify(String token) throws Exception {
21 String[] parts = token.split("\\.");
22 if (parts.length != 3) {
23 throw new SecurityException("malformed token");
24 }
25 String signingInput = parts[0] + "." + parts[1];
26 byte[] expected = hmac(signingInput);
27 byte[] provided = DECODER.decode(parts[2]);
28 if (!MessageDigest.isEqual(expected, provided)) {
29 throw new SecurityException("signature mismatch");
30 }
31 Map<String, Object> claims = MAPPER.readValue(DECODER.decode(parts[1]),
32 new TypeReference<Map<String, Object>>() {});
33 Object exp = claims.get("exp");
34 if (exp instanceof Number n && Instant.now().getEpochSecond() > n.longValue()) {
35 throw new SecurityException("token expired");
36 }
37 return claims;
38 }
39
40 private byte[] hmac(String data) throws Exception {
41 Mac mac = Mac.getInstance("HmacSHA256");
42 mac.init(key);
43 return mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
44 }
45
46 private String encode(Object value) throws Exception {
47 return ENCODER.encodeToString(MAPPER.writeValueAsBytes(value));
48 }
49}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A JWT is just three base64url segments joined by dots, with the last being an HMAC over the first two.
- 2Signature comparison must use a constant-time check like MessageDigest.isEqual to avoid timing attacks.
- 3Verification is only trustworthy when it re-derives the signature from the received bytes and also enforces expiry.
Related explainers
java
@Component @Converter public class EncryptedStringConverter implements AttributeConverter<String, String> {
Transparent column encryption in Spring & JPA
encryption
aes-gcm
jpa-converter
Advanced
10 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
java
package com.acme.billing.config; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties;
Feature-flagged beans with Spring @ConditionalOnProperty
feature-flags
conditional-beans
strategy-pattern
Intermediate
5 steps
java
public static Map<String, String> parseCookieHeader(String header) { Map<String, String> cookies = new LinkedHashMap<>(); if (header == null || header.isBlank()) { return cookies;
Parsing an HTTP Cookie header in Java
string-parsing
http
url-decoding
Intermediate
6 steps
java
public class TimedSocketReader { private static final int READ_TIMEOUT_MS = 5_000; private static final int CONNECT_TIMEOUT_MS = 3_000;
Reading a socket with connect and read timeouts
sockets
timeouts
io
Intermediate
8 steps
java
public final class EncodingDetector { public enum Encoding { UTF_8, UTF_16LE, UTF_16BE, UTF_32LE, UTF_32BE, ASCII, UNKNOWN
Detecting text encoding from raw bytes in Java
byte-manipulation
encoding-detection
bitwise-operations
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/how-hmac-signed-jwts-are-created-and-verified-explained-java-604b/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.