top of page

Implementing JWT Authentication and Authorization in Spring Boot

Updated: Dec 6, 2022


JWT Token:


JSON Web Token is a secure and open standard for token authentication, it is used for encoding information that is securely transmitted as a JSON object.

Although JWTs can be encrypted to also provide secrecy between parties, we will focus on signed tokens. Signed tokens can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.


JSON Web Token structure:

JSON Web Tokens consists of three parts separated by dots (.),

They are:

Header:

The header typically consists of two parts: the Hashing algorithm being used(e.g., HMAC SHA 256) and the type of the token(JWT).

Payload:


The payload contains statements about the entity attributes which are called claims. Claims are statements about an entity (typically, the user) and additional data. There are three types of claims: registered, public, and private claims.

Signature:


The signature is used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn't changed along the way. To create the signature part you have to take the encoded header, the encoded payload, a secret key, the algorithm specified in the header, and sign that.


Securing APIs with JWT Token:


JSON Web Tokens are used to authenticate users on any web or mobile application. It enables backend APIs to accept requests by validating the contents of these JWTs. i.e., applications that use JWT tokens no longer have to hold cookies or other session data about their users. This characteristic facilitates scalability while keeping applications secure.

During the authentication process, when a user successfully logs in using their credentials, a JSON Web Token is returned and must be saved in local storage. Whenever the user wants to access an API, the user agent must send the JWT, usually in the Authorization header using the Bearer schema, along with the request.

When a backend server receives a request with a JWT, the first thing to do is to validate the token. This consists of a series of steps, and if any of these fails then, the request has to be rejected. The following list shows the validation steps needed:

  • Check that the JWT is well-formed.

  • Check the signature.

  • Validate the standard claims.

  • Check the Client permissions (scopes).

The Authentication Filter:


The first element that we are going to create is the class responsible for the authentication process. We are going to call this class JWTAuthenticationFilter, and we will implement it with the following code:


The Authorization Filter:

As we have implemented the filter responsible for authenticating users, we now need to implement the filter responsible for user authorization. We create this filter as a new class, called JWTAuthorizationFilter.


AppSecurityConfig:

This class extends the WebSecurityConfigurerAdapter. This is a convenience class that allows customization to both WebSecurity and HttpSecurity.

package com.User.metadata.security;
import org.springframework.beans.factory.annotation.Autowired;
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
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.authentication.UsernamePasswordAuthenticationFilter;
import com.User.metadata.config.JwtAuthenticationEntryPoint;
import com.User.metadata.util.JwtRequestFilter;

@Configuration
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

	@Autowired
	private JwtRequestFilter jwtRequestFilter;

	@Autowired
	private UserDetailsServiceImpl userDetailsService;

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

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {	auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
	}
	
	@Override
protected void configure(HttpSecurity http) throws Exception {

http.csrf().disable();				         http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/User/register", "/User/logout","/User/login").permitAll().anyRequest().authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint);
http.addFilterBefore(jwtRequestFilter,UsernamePasswordAuthenticationFilter.class);
	}

	@Override
	@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
 	return super.authenticationManagerBean();
	}
}

JwtTokenUtil:


The JwtTokenUtil is responsible for performing JWT operations like creation and validation. It makes use of the io.jsonwebtoken. JWTs for achieving this.

package com.User.metadata.util;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import com.User.metadata.security.UserDetailsServiceImpl;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Component
public class JwtTokenUtil {

	@Value("${jwtconfig.jwtSecret}")
	private String secretKey;
	
	@Value("${jwtconfig.tokenIssuer}")
	private String issuer;
	
	@Autowired
	private UserDetailsServiceImpl userDetailsServiceImpl;

	@PostConstruct
	protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
	}

public String getUsernameFromAccessToken(String token) {		return getClaimFromAccessToken(token, Claims::getSubject);
	}

public Date getExpirationDateFromAccessToken(String token) {
return getClaimFromAccessToken(token, Claims::getExpiration);
	}

	public <T> T getClaimFromAccessToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromAccessToken(token);
return claimsResolver.apply(claims);
	}

private Claims getAllClaimsFromAccessToken(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
	}

private Boolean isAccessTokenExpired(String token) {
final Date expiration = getExpirationDateFromAccessToken(token);
return expiration.before(new Date());
	}

public String generateAccessToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return doGenerateAccessToken(claims, userDetails.getUsername());
	}

private String doGenerateAccessToken(Map<String, Object> claims, String subject) {
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())).setExpiration(new Date(System.currentTimeMillis() + 1000 * 300))
.signWith(SignatureAlgorithm.HS256, secretKey).compact();
	}
	
public Boolean validateAccessToken(String token, UserDetails userDetails) {
final String username = getUsernameFromAccessToken(token);
return (username.equals(userDetails.getUsername()) && !isAccessTokenExpired(token));
	}
}

JWT RequestFilter:


The JwtRequestFilter extends the Spring Web Filter OncePerRequestFilter class. For any incoming request, this Filter class gets executed. It checks if the request has a valid JWT token. If it has a valid JWT Token, then it sets the authentication in context to specify that the current user is authenticated.

package com.User.metadata.util;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.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 com.User.metadata.security.UserDetailsServiceImpl;
import io.jsonwebtoken.ExpiredJwtException;

@Component
public class JwtRequestFilter extends OncePerRequestFilter {

	@Autowired
	private UserDetailsServiceImpl userDetailsServiceImpl;

	@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;
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
	try {
username = jwtTokenUtil.getUsernameFromAccessToken(jwtToken);
			} 
	catch (IllegalArgumentException e) {
	System.out.println("Unable to get JWT Token");
			} 
	catch (ExpiredJwtException e) {
	System.out.println("JWT Token has expired");
			}
		} else {
	logger.warn("JWT Token does not begin with Bearer String");
		}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsServiceImpl.loadUserByUsername(username);
if (jwtTokenUtil.validateAccessToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));	SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
			}
		}
		chain.doFilter(request, response);
	}
}

Generate a JSON Web Token:


Create a POST request with URL localhost:3008/User/login. The body should have a valid username and password. In our case, the userEmail is admin@gmail.com in use and the password is Admin@123. 


Validate the JSON Web Token:


Try accessing the URL localhost:3008/User/userDetails using the above-generated token in the header as follows:


Invalid JSON Web Token:


For Invalid JSON Web Token, we get the response as Unauthorized with status code 401


I hope you enjoyed reading this information.

Leave me a comment, if you want to discuss any further.


Deepak Patnala

Associate Trainee – Cloud Solutions

Digital Transformation

m. +91 9704918432

Recent Posts

See All
What is BDD ?

Behavior-Driven Development (BDD) is a software development process that aims to bridge the gap between technical and non-technical...

 
 
 

2 Comments


Mickey Mickey
Mickey Mickey
Jun 15, 2020

Good Work Man..! It helped me in my current task. Thank You. Keep posting. :-) :-)

Like

Pradeep Kumar
Pradeep Kumar
Jun 13, 2020

Nice Article😊😊😊

Like
Post: Blog2_Post
bottom of page