JWT Authentication In SpringBoot

JWT Authentication In SpringBoot

What is JWT?

JWT (JSON Web Token) is a compact, URL-safe token used to securely transfer claims (like user identity) between client and server — usually after login

❓ Why Use JWT?

In modern apps (especially REST APIs and mobile apps), we want stateless authentication. That means:

  • No session storage on the server

  • Every request carries its own proof (the token)

JWT makes this easy by:

  • Issuing a token when user logs in

  • Validating that token on each request

  • Letting us avoid database checks on every call

🛠️ Is JWT the Only Option?

No — other methods include:

  • Session-based authentication (used in web apps)

  • OAuth 2.0 (for delegated access, like logging in via Google)

  • API keys (used for internal systems)

But for most Spring Boot REST APIs, JWT is the go-to standard.

✅ What We’ll Build

We created a working Spring Boot app with:

  • /auth/login → generates access + refresh token

  • /auth/refresh → gets a new access token using refresh token

  • /api/user → a protected API endpoint

📦 Technologies Used

  • Java 17

  • Spring Boot 3

  • Spring Security

  • JJWT (io.jsonwebtoken)

  • IntelliJ IDEA

📂 Folder Structure

JWT in Spring Boot

1. SecurityConfig.java

package com.example.jwtauthdemo.config;

import com.example.jwtauthdemo.util.JwtUtil;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.List;

@Configuration
public class SecurityConfig {

    @Autowired
    private JwtUtil jwtUtil;

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

        return http.build();
    }

    @Bean
    public OncePerRequestFilter jwtAuthenticationFilter() {
        return new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                    throws ServletException, IOException {

                String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
                if (authHeader != null && authHeader.startsWith("Bearer ")) {
                    String token = authHeader.replace("Bearer ", "");
                    try {
                        Claims claims = jwtUtil.validateToken(token);
                        String username = claims.getSubject();

                        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
                                username, null, List.of(new SimpleGrantedAuthority("ROLE_USER")));
                        SecurityContextHolder.getContext().setAuthentication(auth);
                    } catch (Exception ignored) {}
                }

                chain.doFilter(request, response);
            }
        };
    }
}


    

2. AuthController.Java

package com.example.jwtauthdemo.controller;

import com.example.jwtauthdemo.model.AuthRequest;
import com.example.jwtauthdemo.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

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

    @Autowired
    private TokenService tokenService;

    @PostMapping("/login")
    public ResponseEntity login(@RequestBody AuthRequest request) {
        // Dummy login
        if ("user".equals(request.getUsername()) && "password".equals(request.getPassword())) {
            return ResponseEntity.ok(tokenService.generateTokens(request.getUsername()));
        } else {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
        }
    }

    @PostMapping("/refresh")
    public ResponseEntity refresh(@RequestParam String refreshToken) {
        try {
            String newAccessToken = tokenService.refreshAccessToken(refreshToken);
            return ResponseEntity.ok(Map.of("accessToken", newAccessToken));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid refresh token");
        }
    }
}


3. UserController.java

package com.example.jwtauthdemo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

    @GetMapping
    public String getUser() {
        return "Welcome, authenticated user!";
    }
}


4. AuthRequest.java

package com.example.jwtauthdemo.model;
public class AuthRequest {
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}


5. InMemoryRefreshTokenStore.java

package com.example.jwtauthdemo.service;

import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class InMemoryRefreshTokenStore {
    private final Map store = new ConcurrentHashMap<>();

    public void save(String refreshToken, String username) {
        store.put(refreshToken, username);
    }

    public String getUsername(String refreshToken) {
        return store.get(refreshToken);
    }

    public void delete(String refreshToken) {
        store.remove(refreshToken);
    }
}

6. TokenService.java

package com.example.jwtauthdemo.service;

import com.example.jwtauthdemo.util.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class TokenService {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private InMemoryRefreshTokenStore tokenStore;

    public Map generateTokens(String username) {
        String accessToken = jwtUtil.generateToken(username, 1000 * 60 * 15);  // 15 min
        String refreshToken = jwtUtil.generateToken(username, 1000 * 60 * 60 * 24 * 7); // 7 days

        tokenStore.save(refreshToken, username);

        return Map.of("accessToken", accessToken, "refreshToken", refreshToken);
    }

    public String refreshAccessToken(String refreshToken) {
        Claims claims = jwtUtil.validateToken(refreshToken);
        String username = tokenStore.getUsername(refreshToken);

        if (username == null || !username.equals(claims.getSubject())) {
            throw new RuntimeException("Invalid refresh token");
        }

        return jwtUtil.generateToken(username, 1000 * 60 * 15);
    }
}


7. JwtUtil.java

package com.example.jwtauthdemo.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;
import java.util.Date;

@Component
public class JwtUtil {
    
    private static final String SECRET_KEY = "Z0D8xgU0TbRqq1zQEfivJXt+eUFrJZrD9r93UzEqVUk=";

    private Key getSigningKey() {
        byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
        return Keys.hmacShaKeyFor(keyBytes);
    }

    public String generateToken(String username, long expiryMillis) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expiryMillis))
                .signWith(getSigningKey(), SignatureAlgorithm.HS256)
                .compact();
    }

    public Claims validateToken(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(getSigningKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }
}

🧪 Test It with Postman

  • Login

    • URL: POST /auth/login

    • Body: { "username": "user", "password": "password" }

  • Call API

    • URL: GET /api/user

    • Header: Authorization: Bearer <accessToken>

  • Access Token Expired?

    • URL: POST /auth/refresh?refreshToken=...

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top