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


































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

As we have already discussed the basic introduction and structure of JWT.
In the previous tutorial, we have implemented JWT Hello World Example. So here, in this tutorial we will be implementing a hello world example using JSON Web Token using MySQL database to store the user credentials.
Here in our example, JWT spring security is configured. First the user will do the validate by passing the username and password to the database.
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 Project Structure

    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-data-jpa</artifactId>
    			</dependency>
    	
    			<dependency>
    				<groupId>mysql</groupId>
    				<artifactId>mysql-connector-java</artifactId>
    				<scope>runtime</scope>
    			</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
    
    	spring.datasource.url = jdbc:mysql://localhost:3300/jwtspring?useSSL=false
    	spring.datasource.username = root
    	spring.datasource.password = ****
    	spring.datasource.platform=mysql
    	spring.datasource.initialization-mode=always
    	
    	spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
    	
    	spring.jpa.hibernate.ddl-auto = create-drop
    	
    
    Now Go to mysql and create the database named jwtspring using the following query:-
    create database jwtspring;
    

    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 repository class can be defined as follows-
    
    package com.codeusingjava.repository;
    import org.springframework.data.jpa.repository.JpaRepository;
    import com.codeusingjava.model.UserDao;
    public interface UserRepository extends JpaRepository<UserDao, Integer> {
        UserDao findByUsername(String username);
    }
    

    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 com.codeusingjava.model.UserDao;
    	import com.codeusingjava.repository.UserRepository;
    	
    	import org.springframework.beans.factory.annotation.Autowired;
    	import org.springframework.security.core.userdetails.UserDetails;
    	import org.springframework.security.core.userdetails.UserDetailsService;
    	import org.springframework.security.core.userdetails.UsernameNotFoundException;
    	import org.springframework.security.crypto.password.PasswordEncoder;
    	import org.springframework.stereotype.Service;
    	
    	import java.util.ArrayList;
    	
    	@Service
    	public class JwtUserDetailsService implements UserDetailsService {
    		@Autowired
    		private UserRepository userDao;
    	
    		@Autowired
    		private PasswordEncoder bcryptEncoder;
    	
    		@Override
    		public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    			UserDao user = userDao.findByUsername(username);
    			if (user == null) {
    				throw new UsernameNotFoundException("User not found with username: " + username);
    			}
    			return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
    					new ArrayList<>());
    		}
    	
    		public UserDao save(UserDao user) {
    			UserDao newUser = new UserDao();
    			newUser.setUsername(user.getUsername());
    			newUser.setPassword(bcryptEncoder.encode(user.getPassword()));
    			return userDao.save(newUser);
    		}
    	}
    

    The JwtController class contains the mapping which is used for the creating the user and validate api is used for validation 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 com.codeusingjava.config.JwtTokenUtil;
    	import com.codeusingjava.model.JwtRequest;
    	import com.codeusingjava.model.JwtResponse;
    	import com.codeusingjava.model.UserDao;
    	import com.codeusingjava.service.JwtUserDetailsService;
    	
    	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.*;
    	
    	@RestController
    	public class JwtController {
    	
    		@Autowired
    		private AuthenticationManager authenticationManager;
    	
    		@Autowired
    		private JwtTokenUtil jwtTokenUtil;
    	
    		@Autowired
    		private JwtUserDetailsService userDetailsService;
    	
    		@RequestMapping(value = "/validate", method = RequestMethod.POST)
    		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));
    		}
    	
    		@RequestMapping(value = "/create", method = RequestMethod.POST)
    		public ResponseEntity<?> saveUser(@RequestBody UserDao user) throws Exception {
    			return ResponseEntity.ok(userDetailsService.save(user));
    		}
    	
    		private void authenticate(String username, String password) throws Exception {
    			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 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 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.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", "/create").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 insomnia applicaion.
Firstly we need to store the username and password in the database using the create api.
We have used BCryptPasswordEncoder for storing our password in the database. The password entered by user will be in the normal form.
The api for creating the user.
Spring Boot validate

After the user gets created , the user is validated using /validate api.
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

Downloads-

Spring Boot + JSON Web Token (JWT)+ MySQL Example