Spring Boot + JSON Web Token (JWT)(2021) Hello World Example | CodeUsingJava


































Spring Boot + JSON Web Token (JWT)(2021) Hello World Example

As we have already discussed the basic introduction and structure of JWT so here, In this tutorial we will be implementing a hello world example using JSON Web Token.
Here in our example, JWT spring security is configured. First the user will do the validate by passing the username and password.
Then a JSON Web Token is returned in response. Now any other request can be validated with the help of JSON Web Token. The steps can be diagrammatically represented as follows-
jwt work flow

Table Of Contents :


  • This will be the standard directory layout for maven project structure-
    Spring Boot Initial Project

    The pom.xml file would be as follows-
    		<?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>2.2.1.RELEASE</version>
    				<relativePath/> <!-- lookup parent from repository -->
    			</parent>
    			<groupId>com.codeusingjava</groupId>
    			<artifactId>springjwt</artifactId>
    			<version>0.0.1-SNAPSHOT</version>
    			<name>springjwt</name>
    			<description>Spring Security Implementation with JWT</description>
    		
    			<properties>
    				<java.version>1.8</java.version>
    			</properties>
    		
    			<dependencies>
    				<dependency>
    					<groupId>org.springframework.boot</groupId>
    					<artifactId>spring-boot-starter-web</artifactId>
    				</dependency>
    		
    				<dependency>
    					<groupId>org.springframework.boot</groupId>
    					<artifactId>spring-boot-starter-test</artifactId>
    					<scope>test</scope>
    					<exclusions>
    						<exclusion>
    							<groupId>org.junit.vintage</groupId>
    							<artifactId>junit-vintage-engine</artifactId>
    						</exclusion>
    					</exclusions>
    				</dependency>
    			</dependencies>
    		
    			<build>
    				<plugins>
    					<plugin>
    						<groupId>org.springframework.boot</groupId>
    						<artifactId>spring-boot-maven-plugin</artifactId>
    					</plugin>
    				</plugins>
    			</build>
    		
    		</project>
    		
    	
    The controller class for the request Mapping hello is as follows-
    package com.codeusingjava.controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    @RestController
    public class HelloController {
        @RequestMapping(value = "/hello", method = RequestMethod.GET)
        public String getEmployees() {
            return "Hello World!";
        }
    }
    	
    The initial output without the configuration of spring security is as follows-
    Initial output

    Spring Security JwtAuthentication

    Now , we configure the spring security JWT authentication in our project and observe the output.
    The project structure can be defined as follows-
    Final Project Structure

    We need to start by creating a Maven pom.xml(Project Object Model) file. The pom.xml file contains the project configuration details.
    	<?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>2.2.1.RELEASE</version>
    			<relativePath/> <!-- lookup parent from repository -->
    		</parent>
    		<groupId>com.codeusingjava</groupId>
    		<artifactId>springjwt</artifactId>
    		<version>0.0.1-SNAPSHOT</version>
    		<name>springjwt</name>
    		<description>Spring Security Implementation with JWT</description>
    	
    		<properties>
    			<java.version>1.8</java.version>
    		</properties>
    	
    		<dependencies>
    			<dependency>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-starter-web</artifactId>
    			</dependency>
    	
    			<dependency>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-starter-security</artifactId>
    			</dependency>
    			<dependency>
    				<groupId>io.jsonwebtoken</groupId>
    				<artifactId>jjwt</artifactId>
    				<version>0.9.1</version>
    			</dependency> 
    	
    			<dependency>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-starter-test</artifactId>
    				<scope>test</scope>
    				<exclusions>
    					<exclusion>
    						<groupId>org.junit.vintage</groupId>
    						<artifactId>junit-vintage-engine</artifactId>
    					</exclusion>
    				</exclusions>
    			</dependency>
    		</dependencies>
    	
    		<build>
    			<plugins>
    				<plugin>
    					<groupId>org.springframework.boot</groupId>
    					<artifactId>spring-boot-maven-plugin</artifactId>
    				</plugin>
    			</plugins>
    		</build>
    	
    	</project>
    	
    

    The application.propeties can be defined as follows-
    Here we define the jwt secret which consist of the header ands the payload for creation of a unique hash.
    	jwt.secret=codeusingjava
    
    Create the model class as follows-
    We would be having two model classes.

    First one for JwtRequest. It will contain the username and password as the parameters.
    	package com.codeusingjava.model;
    
    	import java.io.Serializable;
    	
    	public class JwtRequest implements Serializable {
    	
    		private static final long serialVersionUID = 5926468583005150707L;
    		
    		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;
    		}
    	}
    

    The another model class i.e. JwtResponse would be for the token that would be act as the response when the user enters the correct username and the password.
    package com.codeusingjava.model;
    
    import java.io.Serializable;
    
    public class JwtResponse implements Serializable {
    
    	private static final long serialVersionUID = -8091879091924046844L;
    	private final String jwt;
    	public JwtResponse(String jwt) {
    		super();
    		this.jwt = jwt;
    	}
    
    	public String getJwt() {
    		return jwt;
    	}	
    }
    
    The JwtTokenUtil class is as follows-

    A JWT token stores all the essential details of the user. Several utility methods are needed to be defined which helps to fetch the user details from the token, generate as well as validate the token.
    package com.codeusingjava.config;
    
    import java.io.Serializable;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.function.Function;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.stereotype.Component;
    
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    
    @Component
    public class JwtTokenUtil implements Serializable {
    
    	private static final long serialVersionUID = -2550185165626007488L;
    	
    	public static final long JWT_VALIDITY = 5*60*60;
    
    	@Value("")
    	private String secret;
    
    	public String getUsernameFromToken(String token) {
    		return getClaimFromToken(token, Claims::getSubject);
    	}
    
    	public Date getIssuedAtDateFromToken(String token) {
    		return getClaimFromToken(token, Claims::getIssuedAt);
    	}
    
    	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);
    		return claimsResolver.apply(claims);
    	}
    
    	private Claims getAllClaimsFromToken(String token) {
    		return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    	}
    
    	private Boolean isTokenExpired(String token) {
    		final Date expiration = getExpirationDateFromToken(token);
    		return expiration.before(new Date());
    	}
    
    	private Boolean ignoreTokenExpiration(String token) {
    		return false;
    	}
    
    	public String generateToken(UserDetails userDetails) {
    		Map<String, Object> claims = new HashMap<>();
    		return doGenerateToken(claims, userDetails.getUsername());
    	}
    
    	private String doGenerateToken(Map<String, Object> claims, String subject) {
    
    		return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
    				.setExpiration(new Date(System.currentTimeMillis() + JWT_VALIDITY*1000)).signWith(SignatureAlgorithm.HS512, secret).compact();
    	}
    
    	public Boolean canTokenBeRefreshed(String token) {
    		return (!isTokenExpired(token) || ignoreTokenExpiration(token));
    	}
    
    	public Boolean validateToken(String token, UserDetails userDetails) {
    		final String username = getUsernameFromToken(token);
    		return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    	}
    }
    

    The JwtUserDetailsService class implements the UserDetails interface which has all the essential details about the user. We can get details about the current logged in user by overriding the specified method.
    Here, the password is stored as BCryptPasswordEncoder. We have not configured any database in this program so the credentials are stored in the program itself.
    The password encoder used in BCryptPasswordEncoder.
    package com.codeusingjava.service;
    
    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 ("codeusingjava".equals(username)) {
    			//password is 123. BCryptPasswordEncoder is used so encoded password is stored.
    			return new User("codeusingjava", "$2a$10$xsahctaQ8BeMF175jkr8LuQoK1xFXC2vNnJlGnLlCV4w0jruWZGR2",
    					new ArrayList<>());
    		} else {
    			throw new UsernameNotFoundException("username not present!!! " + username);
    		}
    	}
    
    }
    

    The JwtController class contains the mapping which is used for the purpose of validation of the credentials of the user. If the user credentials matched, then a JWT token is generated in response else the specific Exception would be thrown..
    package com.codeusingjava.controller;
    
    import java.util.Objects;
    
    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.security.core.userdetails.UserDetailsService;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.codeusingjava.config.JwtTokenUtil;
    import com.codeusingjava.model.JwtRequest;
    import com.codeusingjava.model.JwtResponse;
    
    @RestController
    public class JwtController {
    
    	@Autowired
    	private AuthenticationManager authenticationManager;
    
    	@Autowired
    	private JwtTokenUtil jwtTokenUtil;
    
    	@Autowired
    	private UserDetailsService userDetailsService;
    
    	@RequestMapping(value = "/validate", method = RequestMethod.POST)
    	public ResponseEntity<?> generateAuthenticationToken(@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 {
    		Objects.requireNonNull(username);
    		Objects.requireNonNull(password);
    		try {
    			authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
    		} catch (DisabledException e) {
    			throw new Exception("USER_DISABLED", e);
    		} catch (BadCredentialsException e) {
    			throw new Exception("INVALID_CREDENTIALS", e);
    		}
    	}
    }
    
    The JwtRequestFilter, as the name suggests, is used for filtering the incoming requests.It contains doFilter method which is to be overridden.
    The class is used to validate the JWT token which is present in the Header section of the request.
    After the validation of the JWT is successful, the response is to be returned.
    package com.codeusingjava.config;
    
    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.codeusingjava.service.JwtUserDetailsService;
    
    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;
    		
    		if (requestTokenHeader != null) {
    			jwtToken = requestTokenHeader.substring(7);
    			try {
    				username = jwtTokenUtil.getUsernameFromToken(jwtToken);
    			} catch (IllegalArgumentException e) {
    				System.out.println("Unable to get JWT Token");
    			} catch (ExpiredJwtException e) {
    				System.out.println("JWT Token has expired");
    			}
    		} 
    
    		
    		if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
    
    			UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
    
    			
    			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 is the first step where the user is checked whether the authenticated person is accessing the mapping and then gives the responser accordingly else exception is thrown with error code 401.
    package com.codeusingjava.config;
    
    import java.io.IOException;
    import java.io.Serializable;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.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 = -7858869558953243875L;
    
    	@Override
    	public void commence(HttpServletRequest request, HttpServletResponse response,
    			AuthenticationException authException) throws IOException {
    
    		response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    	}
    }
    
    
    Creating the configuration class for spring security:-
    All the configurations related to HttpSecurity are done inside the configure method().
    For the process of authentication of user, AuthenticationManagerBuilder is used.
    As we are using mysql database to store users and their authorities, so we use jdbcAuthentication.
    WebSecurityConfigurerAdapter is a class which provides several methods by which httpSecurity and WebSecurity can be customised.
    PasswordEncoder specifies whether the password is to be stored in its original form or by the manner the password is to be encoded.
    	package com.codeusingjava.config;
    
    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.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    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.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    	@Autowired
    	private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    
    	@Autowired
    	private UserDetailsService jwtUserDetailsService;
    
    	@Autowired
    	private JwtRequestFilter jwtRequestFilter;
    
    	@Autowired
    	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    		auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
    	}
    
    	@Bean
    	public PasswordEncoder passwordEncoder() {
    		return new BCryptPasswordEncoder();
    	}
    
    	@Bean
    	@Override
    	public AuthenticationManager authenticationManagerBean() throws Exception {
    		return super.authenticationManagerBean();
    	}
    
    	@Override
    	protected void configure(HttpSecurity httpSecurity) throws Exception {
    		httpSecurity.csrf().disable()
    				.authorizeRequests().antMatchers("/validate").permitAll().antMatchers(HttpMethod.OPTIONS, "/**")
    				.permitAll().
    						anyRequest().authenticated().and().
    				
    						exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
    				.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    
    		httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    	}
    }
    
    
If we now run the application we get the output as follows-
Checking the api in the applicaion.
The username and password used are configured in our program.
We have used BCryptPasswordEncoder for storing our password. Original password is 123.
Spring Boot validate

Sending the token to the header of the request mapping after successful validation.
Spring Boot mapping output

In case the user tries to login to hello mapping without passing the JWT, he would not be able to acess the mapping and error would be shown.
Without jwt