Spring Cloud Security With JWT and MySQL

Home / Spring Cloud Security With JWT and MySQL

In this article we see about Spring Cloud Security. As we know security is the first concern when we work on any project, we don’t want to compromise with user credentials so we have to more concern about security.

JWT ( Json Web Token ) is one of the feature Spring Cloud Security. Any API can be accessed only if user has valid signed token otherwise he will not able to access that APIs.

JWT ( Json Web Token ) consist of three parts. And Structure will be like ( Header.Payload.Secret ). For More Go to ( visit jwt.io )

  1. Header ( Consist of typ that always be JWT and a Hashing algo )
  2. Payload ( Consist of claims or Information )
  3. Signature ( Secret Key that you provide to signed token )

We can use JWT in Spring Boot using below steps:

  1. Add required dependency in your pom.xml file.
  2. Add MySQL configuration in your application.properties file.
  3. Create a class where you define some constatnts like JWT Secret and expire time for JWT token.
  4. Create a utility Class where we can define claim, generate or validate JWT tokens for user.
  5. Create a DAO class where you can perform MySQL activity.
  6. Create a class that implements UserDetailsService and perform user authentication by getting userName and password of user.
  7. create a controller class where we can provide endPoint for getting our Token if authentication successful.
  8. Create Two Model class one is for getting userName and password and other is for JWT Response.
  9. Create a Filter class that perform JWT validation on each request.
  10. create a class that implements AuthenticationEntryPoint for reject every unauthenticated request and Send 401 Error Code for unauthorised user.
  11. Create a class that extends WebSecurityConfigurerAdapter and provide security configuration for endPoint that we want to secure.
  12. After This we will run our application and make a Post request with username and password to authenticate API that we define in controller to get JWT Token.
  13. Now add this Token to your Header with Bearer keyword to get response from Secured APIs. You will get response only if you pass a valid Token.

Follow below steps:

1- Add required dependency in your pom.xml file.

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</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-devtools</artifactId>
 <scope>runtime</scope>
 <optional>true</optional>
</dependency>

<dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <scope>runtime</scope>
</dependency>

2- Add MySQL configuration in your application.properties file.

spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/springBootSecurityMySQL
spring.datasource.username=vasu
spring.datasource.password=12345@
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

3- Create a class where you define some constatnts like JWT Secret and expire time for JWT token.

package com.javadream.constant;

public class JwtConstatnt {

	public static final String JWT_SECRET = "123@@#&amp;javadream.in#$%";
	public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60;
}

4- Create a utility Class where we can define claim, generate or validate JWT tokens for user.

package com.javadream.utility;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.UserDetails;

import com.javadream.constant.JwtConstatnt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Configuration
public class JwtTokenUtility implements Serializable{

	private static final long serialVersionUID = -9172337479697036292L;
	private static final Logger logger = LoggerFactory.getLogger(JwtTokenUtility.class);
	
	private static final String JWT_SECRET = JwtConstatnt.JWT_SECRET;
	private static final long JWT_TOKEN_VALIDITY = JwtConstatnt.JWT_TOKEN_VALIDITY;
	
	     //get username from jwt token
		public String getUsernameFromToken(String token) {
			return getClaimFromToken(token, Claims::getSubject);
		}

		public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
			final Claims claims = getAllClaimsFromToken(token);
			return claimsResolver.apply(claims);
		}
		
		//for get any information from token we will need the secret key
		private Claims getAllClaimsFromToken(String token) {
			return Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJws(token).getBody();
		}
		
		//check if the token has expired
		private Boolean isTokenExpired(String token) {
			final Date expiration = getExpirationDateFromToken(token);
			return expiration.before(new Date());
		}
		
		//retrieve expiration date from jwt token
		public Date getExpirationDateFromToken(String token) {
			return getClaimFromToken(token, Claims::getExpiration);
		}
		
		//generate token for user
		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_TOKEN_VALIDITY * 1000))
					.signWith(SignatureAlgorithm.HS512, JWT_SECRET).compact();
		}
		
		//validate token
		public Boolean validateToken(String token, UserDetails userDetails) {
			final String username = getUsernameFromToken(token);
			return (username.equals(userDetails.getUsername()) &amp;&amp; !isTokenExpired(token));
		}
}

5- Create a DAO class where you can perform MySQL activity.

package com.javadream.dao;

import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Repository;

import com.javadream.model.UserBean;

@Repository
public class UserDAO {
	
	private static final Logger logger = LoggerFactory.getLogger(UserDAO.class);
	
	@Autowired
    private PasswordEncoder encoder;

	@Autowired
	private JdbcTemplate template;
	
	public UserBean getUserInfo(String userName) {
		try {
			String query = "select * from userDetail where userName=?";
			List<Map<String, Object>> userResponse = template.queryForList(query, new Object[] {userName});
			logger.info("vvv::  userResponse= "+userResponse);
			if(userName !=null) {
				UserBean userBean = new UserBean(userResponse.get(0).get("username").toString(),
						userResponse.get(0).get("password").toString());
				return userBean;
			}
		} catch (Exception e) {
			logger.error(e.getStackTrace().toString());
		}
		return null;
	}
	
	   public int insertUser(UserBean user) {
	        try {
	            String userName = user.getUserName();
	            String pass = user.getPassword();

	            logger.info("vvv::  userName= "+userName + " ,pass= "+pass );
	            int updateStatus = template.update("insert into userDetail(username,password) values(?,?)", new Object[] {userName,encoder.encode(pass)});
	            logger.info("vvv::  updateStatus= "+updateStatus);
	            if(updateStatus == 1) {
	             return 1;
	            }
	            return 0;
	        } catch (Exception e) {
	            e.printStackTrace();
	            return 0;
	        }
	    }
}

6- Create a class that implements UserDetailsService and perform user authentication by getting userName and password of user.

package com.javadream.service;

import java.util.ArrayList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.stereotype.Service;

import com.javadream.dao.UserDAO;
import com.javadream.model.UserBean;

@Service
public class JwtUserDetailService implements UserDetailsService {

	private static final Logger logger = LoggerFactory.getLogger(JwtUserDetailService.class);

	@Autowired
	private UserDAO dao;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		try {
			UserBean userInfo = dao.getUserInfo(username);
			logger.info("vvv::  userInfo= " + userInfo);
			if (userInfo != null) {
				UserDetails userDetails = (UserDetails) new org.springframework.security.core.userdetails.User(
						userInfo.getUserName(), userInfo.getPassword(), new ArrayList<>());
				return userDetails;
			} else {
				throw new UsernameNotFoundException("User not found with username: " + username);
			}
		} catch (Exception e) {
			logger.error(e.getMessage());
		}
		return null;
	}

}

7- create a controller class where we can provide endPoint for getting our Token if authentication successful.

package com.javadream.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.javadream.dao.UserDAO;
import com.javadream.model.JwtResponse;
import com.javadream.model.UserBean;
import com.javadream.service.JwtUserDetailService;
import com.javadream.utility.JwtTokenUtility;

@RestController
public class JwtAuthenticationController {

	private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationController.class);

	@Autowired
	private AuthenticationManager authenticationManager;

	@Autowired
	private JwtTokenUtility jwtTokenUtil;

	@Autowired
	private JwtUserDetailService userDetailService;
	
	@Autowired
	private UserDAO dao;

	@PostMapping("/authenticateAPI")
	public ResponseEntity<?> createAuthenticationToken(@RequestBody UserBean authenticationRequest) {
		try {
			logger.info("vvv::  Loading createAuthenticationToken for UserBean= " + authenticationRequest);
			authenticateUser(authenticationRequest.getUserName(), authenticationRequest.getPassword());
			final UserDetails userDetails = userDetailService.loadUserByUsername(authenticationRequest.getUserName());

			final String token = jwtTokenUtil.generateToken(userDetails);

			return ResponseEntity.ok(new JwtResponse(token));
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		
	}
	
	@PostMapping("/insert")
    public String insert(@RequestBody UserBean user) {
        try {
            logger.info("vvv::  user= "+user);
            if(user == null) {
                return "Some technical error try again";
            }
            int insertUser = dao.insertUser(user);
            return "Insert Status= "+insertUser;
        } catch (Exception e) {
            e.printStackTrace();
            return "Some Technical Error Try Again";
        }
         
    }

	private void authenticateUser(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);
		}
	}
}

Create Another Controller Whose APIs are secured.

package com.javadream.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SecureController {

	
	@GetMapping("/SecureAPI")
	public String getSecureEndPoint() {
		return "Nice try! You have learned Spring Boot JWT Security";
	}
}

8- Create Two Model class one is for getting userName and password and other is for JWT Response.

package com.javadream.model;

public class UserBean {

	private String userName;
	private String password;

	public UserBean() {
		super();
	}

	public UserBean(String userName, String password) {
		super();
		this.userName = userName;
		this.password = password;
	}

	@Override
	public String toString() {
		return "UserBean [userName=" + userName + ", password=" + password + "]";
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

}

package com.javadream.model;

import java.io.Serializable;

public class JwtResponse implements Serializable {

	private static final long serialVersionUID = 2624374270511956111L;

	private final String jwttoken;

	public JwtResponse(String jwttoken) {
		this.jwttoken = jwttoken;
	}

	public String getToken() {
		return this.jwttoken;
	}
}

9- Create a Filter class that perform JWT validation on each request.

package com.javadream.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.javadream.service.JwtUserDetailService;

import com.javadream.utility.JwtTokenUtility;

import io.jsonwebtoken.ExpiredJwtException;

@Component
public class JwtFilter extends OncePerRequestFilter {
	private static final Logger logger = LoggerFactory.getLogger(JwtFilter.class);

	@Autowired
	private JwtTokenUtility jwtTokenUtil;

	@Autowired
	private JwtUserDetailService jwtUserDetailsService;

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		final String requestTokenHeader = request.getHeader("Authorization");

		String username = null;
		String jwtToken = null;

		if (requestTokenHeader != null &amp;&amp; requestTokenHeader.startsWith("Bearer ")) {
			jwtToken = requestTokenHeader.substring(7);
			try {
				username = jwtTokenUtil.getUsernameFromToken(jwtToken);
			} catch (IllegalArgumentException e) {
				System.out.println("Unable to get JWT Token for this user");
			} catch (ExpiredJwtException e) {
				System.out.println("JWT Token has been expired");
			}
		} else {
			logger.warn("JWT Token do not start with Bearer keyword");
		}

		// Once we get the token validate it.
		if (username != null &amp;&amp; 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);
			}
		}
		filterChain.doFilter(request, response);

	}

}

10- create a class that implements AuthenticationEntryPoint for reject every unauthenticated request and Send 401 Error Code for unauthorised user.

package com.javadream.service;

import java.io.IOException;
import java.io.Serializable;

import javax.servlet.ServletException;
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 {

	@Override
	public void commence(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException authException) throws IOException, ServletException {
		response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");

	}

}

11- Create a class that extends WebSecurityConfigurerAdapter and provide security configuration for endPoint that we want to secure.

package com.javadream.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.web.builders.HttpSecurity;
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;

import com.javadream.filter.JwtFilter;
import com.javadream.service.JwtAuthenticationEntryPoint;

@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter{

	@Autowired
	private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

	@Autowired
	private UserDetailsService jwtUserDetailsService;

	@Autowired
	private JwtFilter jwtRequestFilter;

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
	}

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

	@Override
	protected void configure(HttpSecurity httpSecurity) throws Exception {

		httpSecurity.csrf().disable()
				.authorizeRequests().antMatchers("/authenticateAPI","/insert").permitAll().
				
				anyRequest().authenticated().and().
				exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
				.and().sessionManagement()
				.sessionCreationPolicy(SessionCreationPolicy.STATELESS);

		httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
	}
}

Now our application has been ready to run, Just run your application and first create a user ( SignUp ). we do not define any security on signup API because everybody allow to do SignUp but rest endPoint are secured. So now make a SignUp request using Postman.

Complete Code on GitHub : SpringBoot Security JWT

Spring Cloud Security

12- After This we will run our application and make a Post request with username and password to authenticate API that we define in controller to get JWT Token.

Spring Cloud Security

13- Now add this Token to your Header with Bearer keyword to get response from Secured APIs. You will get response only if you pass a valid Token.

Spring Cloud Security

You may also like

Spring Boot Security With MySql

spring batch example

Spring Boot with Apache kafka.

File Upload in Spring Boot

HTTPS in Spring Boot

Help Others, Please Share

About Author

1 Comment
  1. Viswajit

    Thanks to author of this tutorial,helpfull tutorial.

Leave a Reply

Your email address will not be published. Required fields are marked *