Spring Boot Security With JWT
In this tutorial, we will create a simple microservice and authenticate it using JWT.
Step 1 : Create a Springboot project with web dependency
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.1</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.sks</groupId>
<artifactId>auth-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>auth-service</name>
<description />
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Step 2 :
BasicController.java
package com.jts;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/basic")
public class BasicController {
@GetMapping("/home")
public String home() {
return "Welcome to home";
}
}
Step 3 :
Test the service

Step 4 :
Now, add below dependencies to your pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
The complete pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.1</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.sks</groupId>
<artifactId>auth-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>auth-service</name>
<description />
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Now apply Security to this service
Step 5 : Add below properties in your application.properties file
Application.properties file
jwt.secret=javatrainingschool
jwt.get.token.uri=/authenticate
Step 6 : Write the following 3 classes as below : JwtRequest.java, JwtResponse.java, AuthenticationController.java
JwtRequest.java
package com.sks;
import java.io.Serializable;
public class JwtRequest implements Serializable {
private static final long serialVersionUID = 6239921560724451905L;
private String username;
private String password;
public JwtRequest()
{
}
public JwtRequest(String username, String password) {
this.setUsername(username);
this.setPassword(password);
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
}
JwtResponse.java
package com.sks;
import java.io.Serializable;
public class JwtResponse implements Serializable {
private static final long serialVersionUID = 8869344122384649326L;
private final String jwttoken;
public JwtResponse(String jwttoken) {
this.jwttoken = jwttoken;
}
public String getToken() {
return this.jwttoken;
}
}
AuthenticationController.java
package com.sks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin
public class AuthenticationController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private JwtUserDetailsService userDetailsService;
//http://localhost:8080/getToken
@PostMapping("/getToken")
public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtRequest authenticationRequest) throws Exception {
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
e.printStackTrace();
} catch (BadCredentialsException e) {
e.printStackTrace();
}
}
}
Step 7 : Write JwtUserDetailsService.java
JwtUserDetailsService.java
In the below class, you need to keep the password in Bcrypt encoded format. You can use any online tool to generate the Bcrypt code of your password. You can use the below link to do so
Note -> No of rounds chosen for this example is 8

package com.sks;
import java.util.ArrayList;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class JwtUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if ("javatrainingschool".equals(username)) {
return new User("javatrainingschool", "$2a$12$sSpi8ok45fgtumyYvTEwm.LIU3oKhxgt6uWBpNv9cdkg/Qp9.fJ5O",
new ArrayList<>());
} else {
throw new UsernameNotFoundException(username + " is not found in the records");
}
}
}
Step 8 : Write the following 4 classes as below : JwtTokenUtil.java, JwtRequestFilter.java, JwtAuthenticationEntryPoint.java, WebSecurityConfig.java
JwtTokenUtil.java
package com.sks;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import javax.crypto.SecretKey;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
@Component
public class JwtTokenUtil implements Serializable {
private static final long serialVersionUID = 1L;
public static final long JWT_TOKEN_VALIDITY = 1 * 60 * 60; // we have kept 1 hour validity
//@Value("${jwt.secret}")
private String secret = "my-super-secret-key-for-jwt-authentication-2026-secure";
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
}
//get username from jwt token
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
//get expiration date from jwt token
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
System.out.println("Claimes: " + claims);
return claimsResolver.apply(claims);
}
//secret key needed
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();
}
//it checks if the token is expired
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
//this generates token
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(userDetails.getUsername());
}
//claims of the token like Issuer, Expiration, Subject, and the ID
//HS512 algorithm is used here to sign the token
//JWT is compacted using compact() to a URL-safe string
public String doGenerateToken(String userName) {
return Jwts.builder()
.subject(userName)
.claim("userType", userName)
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
.signWith(getSigningKey(), Jwts.SIG.HS384)
.compact();
}
//this validates token
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
JwtRequestFilter.java
package com.sks;
import java.io.IOException;
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.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import io.jsonwebtoken.ExpiredJwtException;
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtUserDetailsService jwtUserDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
// JWT Token is passed in the form "Bearer <token>". Thus, let's remove Bearer
//word and get the Token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
System.out.println("from token : " + jwtToken);
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
System.out.println("name from token : " + username);
} catch (IllegalArgumentException e) {
System.out.println("Failed to get token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token is expired");
}
} else {
System.out.println("JWT Token does not begin with Bearer word");
}
//Validate the token
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = jwtUserDetailsService.loadUserByUsername(username);
// on valid token configure Spring Security to authentication
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
JwtAuthenticationEntryPoint.java
package com.sks;
import java.io.IOException;
import java.io.Serializable;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = 1L;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
WebSecurityConfig.java
package com.sks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
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
@EnableWebSecurity
public class WebSecurityConfig {
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(
HttpSecurity http,
JwtUserDetailsService jwtService,
JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint) throws Exception {
http
.csrf(csrf -> csrf.disable())
.exceptionHandling(exception -> exception
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.requestMatchers(
"/getToken",
"/register",
"/error"
).permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
Step 9 : Generate the token and test the service
Hit the below url to generate the jwt token

Now, the home service will not be accessible without a token since it has been secured

To access it, you need to send the token as header as shown below

Augh! This tutorial has eaten up my mind. I am going to enjoy my coffee now. And you??
