Here’s a clear, complete explanation + working structure for implementing JWT Authentication in Spring Boot (Spring Security 6 / Boot 3).
JWT Authentication with Spring Boot Example
You’ll learn:
- How JWT works
- Complete flow (with code)
- Key classes
- Example endpoints
🧠 What Is JWT Authentication?
JWT (JSON Web Token) = a secure, compact token that verifies a user’s identity.
It’s used to achieve stateless authentication — no sessions stored on the server.
🧩 How It Works (Flow)
- User sends credentials →
POST /auth/login - Server authenticates user
- Server generates a JWT token and sends it back
- User includes that token in future requests:
Authorization: Bearer <jwt-token> - Server validates the token on every request (no DB lookup needed)
- If valid → user is authenticated
⚙️ Project Setup
Dependencies (Gradle)
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
}
1️⃣ JwtUtil – Token Generator & Validator
package com.example.security.jwt;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
@Component
public class JwtUtil {
private final Key key;
private final long expiration;
public JwtUtil(@Value("${jwt.secret}") String secret,
@Value("${jwt.expiration-ms}") long expiration) {
this.key = Keys.hmacShaKeyFor(secret.getBytes());
this.expiration = expiration;
}
public String generateToken(String username) {
Date now = new Date();
Date expiry = new Date(now.getTime() + expiration);
return Jwts.builder()
.setSubject(username)
.setIssuedAt(now)
.setExpiration(expiry)
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}
public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
return true;
} catch (JwtException e) {
return false;
}
}
public String getUsername(String token) {
return Jwts.parserBuilder().setSigningKey(key).build()
.parseClaimsJws(token).getBody().getSubject();
}
}
2️⃣ Security Configuration
package com.example.security.config;
import com.example.security.filter.JwtAuthFilter;
import com.example.security.jwt.JwtUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
public class SecurityConfig {
private final JwtUtil jwtUtil;
private final UserDetailsService userDetailsService;
public SecurityConfig(JwtUtil jwtUtil, UserDetailsService userDetailsService) {
this.jwtUtil = jwtUtil;
this.userDetailsService = userDetailsService;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public JwtAuthFilter jwtAuthFilter() {
return new JwtAuthFilter(jwtUtil, userDetailsService);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**").permitAll()
.anyRequest().authenticated())
.addFilterBefore(jwtAuthFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
3️⃣ JWT Authentication Filter
package com.example.security.filter;
import com.example.security.jwt.JwtUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
private final UserDetailsService userDetailsService;
public JwtAuthFilter(JwtUtil jwtUtil, UserDetailsService userDetailsService) {
this.jwtUtil = jwtUtil;
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String header = request.getHeader("Authorization");
String token = null;
if (StringUtils.hasText(header) && header.startsWith("Bearer ")) {
token = header.substring(7);
}
if (token != null && jwtUtil.validateToken(token)) {
String username = jwtUtil.getUsername(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(request, response);
}
}
4️⃣ AuthController – Login Endpoint
package com.example.security.controller;
import com.example.security.jwt.JwtUtil;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/auth")
public class AuthController {
private final AuthenticationManager authManager;
private final JwtUtil jwtUtil;
public AuthController(AuthenticationManager authManager, JwtUtil jwtUtil) {
this.authManager = authManager;
this.jwtUtil = jwtUtil;
}
@PostMapping("/login")
public String login(@RequestBody LoginRequest request) {
Authentication authentication = authManager.authenticate(
new UsernamePasswordAuthenticationToken(request.username, request.password)
);
if (authentication.isAuthenticated()) {
return jwtUtil.generateToken(request.username);
} else {
throw new RuntimeException("Invalid credentials");
}
}
public record LoginRequest(String username, String password) {}
}
5️⃣ Example Protected Endpoint
package com.example.security.controller;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class DemoController {
@GetMapping("/hello")
public String hello() {
return "Hello from secured endpoint!";
}
}
6️⃣ application.properties
jwt.secret=ThisIsMySuperSecretJwtKey123456789
jwt.expiration-ms=3600000
🔍 How to Test
- Start your Spring Boot app.
- Call
/auth/login:
curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"user","password":"password"}'→ You’ll get a JWT token.
3. Call protected endpoint:
curl http://localhost:8080/api/hello \
-H "Authorization: Bearer <your-token>"✅ Should return "Hello from secured endpoint!"
🚀 Summary
| Step | Description |
|---|---|
| Build token | JwtUtil.generateToken() |
| Authenticate | /auth/login |
| Validate token | JwtAuthFilter |
| Secure routes | SecurityFilterChain |
| Access | Authorization: Bearer <token> |
Production considerations (quick checklist)
- Test JWT expiration and edge cases.
- Use a long, random secret (rotate periodically), store in environment variables or secret manager.
- Use HTTPS always.
- Consider refresh tokens if you want long-lived sessions.
- Add token revocation / blacklist if you need immediate logout capability.
- Add claims (roles, tenant id) into JWT as needed — keep size reasonable.
- Implement exception handling in the filter for expired/invalid tokens and map to proper HTTP status codes.
- Use
@PreAuthorize/ method security for fine-grained access control.
Read other awesome articles in Medium.com or in akcoding’s posts.
OR
Join us on YouTube Channel
OR Scan the QR Code to Directly open the Channel 👉

