In this lecture we'll cover:
- How to configure Spring Boot to work with JWT.
- Implementing JWT token generation after successful authentication.
- Setting up a security filter to verify tokens in requests.
- Managing access to protected resources using JWT and Spring Security.
Ready? Brew some coffee and let's go!
Configuring Spring Boot to work with JWT
First, create a basic Spring Boot project. Make sure your pom.xml or build.gradle includes the necessary dependencies:
Dependencies
If you're using Maven, add this to your pom.xml:
<dependencies>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JWT library -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
Or, if you're using Gradle:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
}
These dependencies will provide the tools you need to implement JWT authentication.
JWT token generation
Let's generate JWT tokens. Create a class JwtTokenUtil that will handle creating and validating tokens.
Implementation of JwtTokenUtil
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtTokenUtil {
private static final String SECRET_KEY = "secret"; // Secret key (don't use such simple keys in real life!)
private static final long EXPIRATION_TIME = 1000 * 60 * 60 * 10; // 10 hours
// Token generation
public String generateToken(String username) {
Map<String, Object> claims = new HashMap<>(); // You can add custom data here
return Jwts.builder()
.setClaims(claims)
.setSubject(username) // Set the username as the token subject
.setIssuedAt(new Date()) // Token issuance date
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) // Expiration time
.signWith(SignatureAlgorithm.HS512, SECRET_KEY) // Use HMAC-SHA512 for signing
.compact();
}
// Token validation (check if token is expired and matches the user)
public boolean validateToken(String token, String username) {
final String tokenUsername = extractUsername(token);
return (username.equals(tokenUsername) && !isTokenExpired(token));
}
// Extract username from token
public String extractUsername(String token) {
return extractAllClaims(token).getSubject();
}
// Check token expiration
public boolean isTokenExpired(String token) {
return extractAllClaims(token).getExpiration().before(new Date());
}
// Extract all claims (token payload)
private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
}
}
In this class we implemented the main methods to work with JWT. Now we can generate tokens, validate them, and extract data from the payload.
Protecting resources with JWT
Now let's set up security. We'll use Spring Security to filter requests and verify tokens.
Security filter setup
Add a filter that will intercept requests, extract the JWT from the request header, and validate it.
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
private final JwtTokenUtil jwtTokenUtil;
public JwtRequestFilter(JwtTokenUtil jwtTokenUtil) {
this.jwtTokenUtil = jwtTokenUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
// Extract the token from the "Authorization: Bearer ..." header
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtTokenUtil.extractUsername(jwt);
}
// Validate the token and set the security context
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
if (jwtTokenUtil.validateToken(jwt, username)) {
// Create authentication based on the token (simplified example)
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
username, null, new ArrayList<>()
);
auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
// Continue the filter chain
chain.doFilter(request, response);
}
}
This filter will validate the token for each request and add user info to the security context.
Spring Security configuration
Create a security configuration class to register our JwtRequestFilter and configure access to protected resources.
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.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtRequestFilter jwtRequestFilter;
public SecurityConfig(JwtRequestFilter jwtRequestFilter) {
this.jwtRequestFilter = jwtRequestFilter;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(HttpSecurity httpSecurity) throws Exception {
return httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
.build();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests()
.requestMatchers("/authenticate").permitAll() // Open access for authentication
.anyRequest().authenticated() // All other requests require authentication
.and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
Authentication endpoint implementation
Create a controller that accepts a username and password, then returns a JWT on successful authentication.
import org.springframework.web.bind.annotation.*;
@RestController
public class AuthController {
private final JwtTokenUtil jwtTokenUtil;
public AuthController(JwtTokenUtil jwtTokenUtil) {
this.jwtTokenUtil = jwtTokenUtil;
}
@PostMapping("/authenticate")
public String authenticate(@RequestBody AuthRequest authRequest) {
// Simple username check (for demo — use UserDetailsService in real apps)
if ("user".equals(authRequest.getUsername()) && "password".equals(authRequest.getPassword())) {
return jwtTokenUtil.generateToken(authRequest.getUsername());
} else {
throw new RuntimeException("Invalid credentials");
}
}
}
// DTO for authentication request
class AuthRequest {
private String username;
private String password;
// Getters and Setters
}
Now, by sending a POST request to /authenticate with valid credentials, you'll get a JWT that can be used to access protected resources.
At this point you have a basic app with JWT authentication. We've built a structure that's easy to scale for more complex projects. In real life, use a database to store users and roles, and add more layers of security checks.
GO TO FULL VERSION