JWT Authentication with Spring Security 6

Learn to implement JWT Authentication with Spring Security 6. Learn about token creation, validation, filters, securing APIs using modern best practices.

๐Ÿš€ What is JWT Authentication?

JWT (JSON Web Token) is a stateless, compact, and secure mechanism for transmitting user authentication data between parties.

A JWT typically consists of three parts:


header.payload.signature

When a user logs in:

  • A JWT is issued and sent to the client.
  • For every secured request, the client includes this token in the Authorization header.
  • The server validates the token and grants access accordingly.

JWT Authentication with Spring Security 6

๐Ÿงฉ Project Structure


com.kscodes.springboot.security.jwt
โ”œโ”€โ”€ config/
โ”‚   โ””โ”€โ”€ SecurityConfig.java
โ”œโ”€โ”€ controller/
โ”‚   โ””โ”€โ”€ AuthController.java
โ”œโ”€โ”€ filter/
โ”‚   โ””โ”€โ”€ JwtAuthenticationFilter.java
โ”œโ”€โ”€ model/
โ”‚   โ””โ”€โ”€ AuthRequest.java
โ”œโ”€โ”€ service/
โ”‚   โ””โ”€โ”€ JwtService.java
โ””โ”€โ”€ SpringBootJwtApplication.java

๐Ÿ“ฆ Maven Dependencies

In pom.xml:



    org.springframework.boot
    spring-boot-starter-security



    org.springframework.boot
    spring-boot-starter-web



    io.jsonwebtoken
    jjwt-api
    0.11.5


    io.jsonwebtoken
    jjwt-impl
    0.11.5
    runtime


    io.jsonwebtoken
    jjwt-jackson
    0.11.5
    runtime


๐Ÿ” Security Configuration

SecurityConfig.java


package com.kscodes.springboot.security.jwt.config;

import com.kscodes.springboot.security.jwt.filter.JwtAuthenticationFilter;
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.User;
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.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtAuthFilter;

    public SecurityConfig(JwtAuthenticationFilter jwtAuthFilter) {
        this.jwtAuthFilter = jwtAuthFilter;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new InMemoryUserDetailsManager(
            User.withUsername("user")
                .password(passwordEncoder().encode("password"))
                .roles("USER")
                .build()
        );
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }
}

๐Ÿงช Auth Request DTO

AuthRequest.java


package com.kscodes.springboot.security.jwt.model;

public class AuthRequest {
    private String username;
    private String password;

    // Getters and setters
}

๐Ÿ”‘ JWT Service

JwtService.java


package com.kscodes.springboot.security.jwt.service;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import java.security.Key;
import java.util.Date;

@Service
public class JwtService {

    private final String SECRET = "mySuperSecretKey12345678901234567890"; // 256-bit key

    public String generateToken(UserDetails userDetails) {
        return Jwts.builder()
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 1 hour
                .signWith(getSignKey(), SignatureAlgorithm.HS256)
                .compact();
    }

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

    public String extractUsername(String token) {
        return extractClaims(token).getSubject();
    }

    private boolean isTokenExpired(String token) {
        return extractClaims(token).getExpiration().before(new Date());
    }

    private Claims extractClaims(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(getSignKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    private Key getSignKey() {
        return Keys.hmacShaKeyFor(SECRET.getBytes());
    }
}

๐Ÿงฑ JWT Authentication Filter

JwtAuthenticationFilter.java


package com.kscodes.springboot.security.jwt.filter;

import com.kscodes.springboot.security.jwt.service.JwtService;
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.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtService jwtService;
    private final UserDetailsService userDetailsService;

    public JwtAuthenticationFilter(JwtService jwtService, UserDetailsService userDetailsService) {
        this.jwtService = jwtService;
        this.userDetailsService = userDetailsService;
    }

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

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

        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }

        jwt = authHeader.substring(7);
        username = jwtService.extractUsername(jwt);

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

            if (jwtService.isTokenValid(jwt, userDetails)) {
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());

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

        filterChain.doFilter(request, response);
    }
}

๐ŸŒ Authentication Controller

AuthController.java


package com.kscodes.springboot.security.jwt.controller;

import com.kscodes.springboot.security.jwt.model.AuthRequest;
import com.kscodes.springboot.security.jwt.service.JwtService;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    private final JwtService jwtService;
    private final AuthenticationManager authenticationManager;
    private final UserDetailsService userDetailsService;

    public AuthController(JwtService jwtService,
                          AuthenticationManager authenticationManager,
                          UserDetailsService userDetailsService) {
        this.jwtService = jwtService;
        this.authenticationManager = authenticationManager;
        this.userDetailsService = userDetailsService;
    }

    @PostMapping("/login")
    public String authenticate(@RequestBody AuthRequest authRequest) {
        authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
        );

        UserDetails userDetails = userDetailsService.loadUserByUsername(authRequest.getUsername());
        return jwtService.generateToken(userDetails);
    }
}

๐Ÿ”’ Secured API Endpoint Example


@RestController
@RequestMapping("/api/user")
public class UserController {

    @GetMapping("/profile")
    public String profile() {
        return "This is a secured profile endpoint.";
    }
}

๐Ÿงช Testing the Flow

1. Authenticate and get token


curl -X POST http://localhost:8080/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"user", "password":"password"}'

๐Ÿ“ฅ Response:
"eyJhbGciOiJIUzI1NiJ9..."

Access secured endpoint


curl http://localhost:8080/api/user/profile \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."

โœ… Response:
This is a secured profile endpoint.

๐Ÿ Conclusion

JWT Authentication with Spring Security 6 in a Spring Boot 3 application offers a stateless and secure mechanism to authenticate and authorize API access. With updated components like SecurityFilterChain and modern JWT libraries, you can implement a clean and efficient security layer with minimal overhead.

๐Ÿ“š References