Billing System using Spring Boot-Integrating JWT Authorization. Access Token and Refresh Token flow

Billing System using Spring Boot-Integrating JWT Authorization. Access Token and Refresh Token flow
Spring boot jwt integration

Welcome back, friends. In this story, I will be going to explain the implementation of JWT in our Spring Boot App. If you are not familiar with Security. I am suggesting to read the following Stories
Enabling default Spring Security in Spring Boot
Enabling Spring Security with a custom user
Spring Boot Basic Authentication step by step implementation

The following are key classes to enable JWT Authorization in our Spring Boot Application.

AuthenticationService
JwtUtils
SecurityConfig
JwtRequestFilter
JwtTokenRepository

Following two artifacts we have to add in our pom.xml file. The first artifact enables the Spring security in our Spring Boot App. The second artifact is for JWT API. This artifact provides JWT API to implement JWT Mechanism.

<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>

If you go through the Basic Authentication in my Stories here, it will great for understanding JWT because I will be using the same authentication configuration except for JWT related code and storing JWT Token in the DB.
What is JWT?
JWT Stands for JSON Web Token. It is a big encrypted string and it has three sections separated by (Dot.). The Example of the JWT structure is below.

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzdXJlc2guY3VzdG9tZXJAZ21haWwuY29tIiwiZXhwIjoxNjA1NTcwOTU2LCJpYXQiOjE2MDU1NjU5NTZ9.NjTWNR2j6hDBHX9VjQI7aTx6Gr3h1pMnt4FxE9BmB3k

What type of data contains behind this JWT String? In the above string, I highlighted 3 sections. Let’s explore the three sections.

The first section is the header section which contains the following attribute.

{
  "alg": "HS256"
}

 

Using this attribute we can know that this JWT encryption using the “HS256” algorithm.

The second section is the Payload section it has the following attributes.

{
  "sub": "suresh.customer@gmail.com",
  "exp": 1605570956,
  "iat": 1605565956
}

This attribute generated by our JWT API. while sending credentials from the client-side for authorization, our back-end System authorizes the credential, if it is valid then, create this JSON structure, In this JSON Structure is having 3 attributes first one is a username. it named it “sub”(Subject) and exp is the expiration time of the generated token i.e. The JWT token has a validity period. It is in a long format. The third one is “iat” which means “issued At”. This  “iat” tells that when JWT token is generated.

The third section is the Signature section. it has signature data generated by the combination of header and payload.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
)

All these 3 sections structured using JSON format so, the JSON encrypted string calling it as JSON Web Token. Now, let’s go for JWT implementation.

Implementation of AuthenticationService

This class must implement using the UserDetailsService interface Like below. Don’t forget to Annotated with @Service Annotation

@Service
public class AuthenticationService implements UserDetailsService {

I have Autowired following classes.

@Autowired
private UserRepository userRepository;

@Autowired
private JwtTokenRepository jwtTokenRepository;

@Autowired
private JwtUtils jwtUtils;

Use of loadUserByName method
I have to override this method from UserDetailsService. In this method, I am fetching the user name using emailId if the user name is found, I am assigning it to the User object. Then, I am initializing a new User object by passing an email id from the user.getEmailId() and password from the user.getPassword() and third parameter I am passing empty authorities. When notice, there are two different User Objects I am handling one User Object is coming from “itgarden.entity” package which I am retrieving using userRepository object and another one is I am initializing a new User Object which is from Spring Security User object. I am assigning the Spring Security User object to the UserDetails object. Finally, this method returns UesrDetails which holds the emailId, password, and Authorities(Roles). Since I am passing empty ArrayList there is no Role.

Use of saveJwt Method
This method simply stores the JWT Token data in the jwt_token table. 

Use of generateAuthResponse Method
This method generates two types of Token one is Access Token and another one is Refresh Token. Based on the Token Type parameter, It will generate either Access Token or Refresh Token. I will explain later points of what is the difference between Access Token and Refresh Token.

Implementation of JwtUtils.java
The use of this class is generating a new JWT Token or processing an existing token.

Some of the important method listed below.

extractUsername(String token, TokenType tokenType)
This method extracts the username from the token. it extracts the user name from the Refresh Token or Access Token based on the TokenType parameter.

isAccessTokenExpired(String token)
This method checks that the given Access Token is expired already.

isRefreshTokenExpired(String token)
This method checks that the given Refresh Token is expired already.

createToken(Map<String, Object> claims, String subject, TokenType tokenType)
This createToken method creates either Access Token or Refresh Token based on the Token type provided in the method parameter. The second parameter is the username which I am setting in Claims Object. Actually while generating token I am passing username, expired data, and Issued Time in the Claims object. Claims object nothing but payload data which the second section from JWT Token.

generateAccessToken(UserDetails userDetails)
This method just initializing new Claims Object and calling the “createToken” Method which we discussed above. This method is for creating an Access token so, I am passing Token type as ACCESS_TOKEN

generateRefreshToken(UserDetails userDetails)
This method just initializing new Claims Object and calling the “createToken” Method which we discussed above. This method is for creating a Refresh token so, I am passing the Token type as REFRESH_TOKEN

Boolean isValidToken(String token, TokenType tokenType)
This method checks the validity of the Token whether it expired or not. 

The above methods are some of the important methods I am using for JWT Token manipulations.

SecurityConfig
This class extends from WebSecurityConfigurerAdapter. This class act as a Spring Security configuration class. This class must be Annotated with @EnableWebSecurity. This Annotation enables this class as a Configuration class for Spring Security. This will do the necessary Spring Security Configuration at the time of starting the server.

We need to override the following method. Using this method we are saying that hey Spring Security, use the “AuthenticationService” class for loading user details for Authorizing.

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

We need to override the following method for configuring endpoint access, session management, and filters.

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
            .authorizeRequests().antMatchers("/api/public/authenticate","/api/public/refreshtoken").permitAll()
            .anyRequest().authenticated()
            .and().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
            http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}

Disabling Cross-Site Request Forgery
http.csrf().disable() 

The following line says that two endpoints visible to the world i.e. these endpoints are available to access publicly.
antMatchers(“/api/public/authenticate”,”/api/public/refreshtoken”).permitAll()
The meaning of this line is, authenticate all endpoint in my application other than the above two endpoints
.anyRequest().authenticated()
Setting session management as Stateless
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
There are several filters may call before handling the Request, here I am
saying that call my “basicRequestFilter” before calling “UsernamePasswordAuthenticationFilter”

http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);

At the time of starting the server, we have to initialize AuthenticationManager by overriding the below method. We are authenticating credentials using the following bean. You can see that respective code in “authenticate” method from PublicUserController.java

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

Initializing PasswordEncoder is mandatory to work spring Security so I am initializing this bean with No Password encoded.

@Bean
public PasswordEncoder passwordEncoder() {
 return NoOpPasswordEncoder.getInstance();
}

This is the configuration for Spring Security. Now we will be going to see the implementation of the “authenticate” endpoint.

UserPublicController
I have defined two Service endpoints in this class. One is “authenticate” and “refreshToken”. The “authenticate” method for validating credential and generating new Access Token and Refresh Token 

I am using “AuthenticationRequestDTO” as a request body that accepts the following attributes. When I request for new Access Token and Refresh token I am using “userName” and “password”. When I request a new Acces Token I am using “refreshToken” Token attribute.

private String userName;
    private String password;
    private String refreshToken;
}
JSON Request Body 
{
    “userName”:”suresh.customer@gmail.com”,
    “password”:”123″
}

The following code snippet is in the authenticate method which validates the given user name password is valid or not. It is very similar to Basic Authentication which I implemented here. Once validation is successful, I start generating JWT Token for the credential.

try {
    authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(authenticationRequest.getUserName(), authenticationRequest.getPassword()));
} catch (BadCredentialsException bce) {
    throw new InvalidUserNamePasswordException(bce.getMessage());
}

In order to understand the above code, please familiar with Basic Authentication here. If the user is not valid, the above code throws BadCredentialException otherwise the below code executes and generates a new Access Token and Refresh Token.

UserDetails userDetails = authenticationService.loadUserByUsername(authenticationRequest.getUserName());
AuthenticationResponseDTO authenticationResponseDTO = authenticationService.generateAuthResponse(userDetails, TokenType.ACCESS_TOKEN);
JwtToken jwtTokenResponse = authenticationService.saveJwt(authenticationResponseDTO);
if (jwtTokenResponse != null) {
    ResponseMessage responseMessage = ResponseMessage.withResponseData(authenticationResponseDTO, "Authentication Success", "Message");
    return new ResponseEntity<ResponseMessage<AuthenticationResponseDTO>>(responseMessage, HttpStatus.OK);
} else {
    ResponseMessage responseMessage = ResponseMessage.withResponseData(new AuthenticationResponseDTO(), "Authentication Failure", "Error");
    return new ResponseEntity<ResponseMessage<AuthenticationResponseDTO>>(responseMessage, HttpStatus.BAD_REQUEST);
}

Explaining the above code step by step…..
After successful validation, I am sending the username as a parameter in “loadUserByUsername” from AuthenticationService. it will return UserDetails which holds the Username
UserDetails userDetails = authenticationService.loadUserByUsername(authenticationRequest.getUserName());
After populating UserDetails, I am calling the following method to generate Access Token and Refresh Token.
authenticationService.generateAuthResponse(userDetails, TokenType.ACCESS_TOKEN);
Since I want to generate Access Token, I am passing Token type as Access Token. Please look at the implementation of the “generateAuthResponse” method for the ACCESS_TOKEN Section.

if(tokenType.equals(TokenType.ACCESS_TOKEN)) {
    String accessToken = jwtUtils.generateAccessToken(userDetails);
    final Claims accessclaims = jwtUtils.extractAllClaims(accessToken);
    String refreshToken = jwtUtils.generateRefreshToken(userDetails);
    final Claims refreshClaims = jwtUtils.extractAllClaims(refreshToken);
    authenticationResponseDTO = new AuthenticationResponseDTO();
    authenticationResponseDTO.setAccessToken(accessToken);
    authenticationResponseDTO.setRefreshToken(refreshToken);
    authenticationResponseDTO.setUserName(userDetails.getUsername());
    authenticationResponseDTO.setAccessTokenExpiration(Utils.convertToLocalDateTime(accessclaims.getExpiration().getTime()));
    authenticationResponseDTO.setRefreshTokenExpiration(Utils.convertToLocalDateTime(refreshClaims.getExpiration().getTime()));
    if (jwtToken != null) {
        authenticationResponseDTO.setId(jwtToken.getId());
    }
}

The below lines of code generating Access token and Refresh token and extracting the claims which are sub,exp, and iat. Sub = username ,exp = Token Expiration data and iat = Token issued date.

String accessToken = jwtUtils.generateAccessToken(userDetails);
final Claims accessclaims = jwtUtils.extractAllClaims(accessToken);
String refreshToken = jwtUtils.generateRefreshToken(userDetails);
final Claims refreshClaims = jwtUtils.extractAllClaims(refreshToken);

After generating the Refresh Token and Access Token, I am initializing the Authentication Response DTO class and setting the following attribute in the Response DTO class.

authenticationResponseDTO = new AuthenticationResponseDTO();
    authenticationResponseDTO.setAccessToken(accessToken);
    authenticationResponseDTO.setRefreshToken(refreshToken);
    authenticationResponseDTO.setUserName(userDetails.getUsername());
    authenticationResponseDTO.setAccessTokenExpiration(Utils.convertToLocalDateTime(accessclaims.getExpiration().getTime()));
    authenticationResponseDTO.setRefreshTokenExpiration(Utils.convertToLocalDateTime(refreshClaims.getExpiration().getTime()));
    if (jwtToken != null) {
        authenticationResponseDTO.setId(jwtToken.getId());
    }

The below line is for , if the JWT Id already exist in the DB, I want update the existing Access token and Refresh Token with newly generated value.

if (jwtToken != null) {
 authenticationResponseDTO.setId(jwtToken.getId());
 }

The following code is very simple. After doing all the processes finally, I am returning the AuthenticationResponseDTO in “UserPublicController” then I am setting these values in the JWTToken entity object to save it in the Database.

JwtToken jwtTokenResponse = authenticationService.saveJwt(authenticationResponseDTO);
if (jwtTokenResponse != null) {
    ResponseMessage responseMessage = ResponseMessage.withResponseData(authenticationResponseDTO, "Authentication Success", "Message");
    return new ResponseEntity<ResponseMessage<AuthenticationResponseDTO>>(responseMessage, HttpStatus.OK);
} else {
    ResponseMessage responseMessage = ResponseMessage.withResponseData(new AuthenticationResponseDTO(), "Authentication Failure", "Error");
    return new ResponseEntity<ResponseMessage<AuthenticationResponseDTO>>(responseMessage, HttpStatus.BAD_REQUEST);
}

Now we have completed the coding part “authenticate” Rest Service method. Using this endpoint we can generate Access Token and Refresh Token. Before going to explore the refresh token endpoint. First, we need to understand why we need to generate an Access Token and Refresh Token.

What is the difference between the Acess token and Refresh token?
While calling the “authenticate” endpoint, it generates both Access Token and Refresh Token. These two token clients should keep their end in a Secure manner. whenever they want to access any endpoint, they have to use Access Token for authorization from the Server. if the Access Token is valid and it is not expired, the server returns a successful response to the client otherwise Server returns an appropriate error to the client. This the purpose of an Access Token. 
What will happen if the Access token is expired or invalid Access token?
Now Refresh Token is coming into the picture, the client must send valid refresh token to the Server saying that hey server my Access token is expired so, use this Refresh token and give me a new Access token. The server validates the Refresh token, if the client provided Refresh token is valid, the Server generates the new Access Token to the client for further Webservice request handling. 

What will happen if the refresh token also expired?
if the refresh token is invalid or expired, the server couldn’t generate a new Access Token instead it will return a valid error. In this case, the Client must re-login using their valid credential and regenerate Access Token and Refresh Token. please check below pictorial diagram for the JWT Authorization flow.
JWT Authorization flow

Exploring refreshtoken Rest Service method endpoint.
If the Access token is expired, we have to request a new Access token to the server by providing a valid refresh token. below is the request body for the refresh token.

{
"refreshToken":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzdXJlc2guY3VzdG9tZXJAZ21haWwuY29tIiwiZXhwIjoxNjA1MjUxNjU4LCJpYXQiOjE2MDUyNDI2NTh9.RKTy4iHBBkOwrtpNhW27S4-ZJX-gvsM7tRGmYZoYVxU"
}

The following code for implementation of “refreshtoken” webservice endpoint.
String refreshToken = authenticationRequest.getRefreshToken();
 //Validate the token while extracting user.
if(jwtUtils.isRefreshTokenExpired(refreshToken)) {

 throw new InvalidTokenException(“Invalid Token: The Refresh Token is Expired”);
 }

Using Refresh Token, I am extracting the user name by “extractUsername” method from jwtUtils class then, I am passing the username in “loadUserByUsername” method which returns “UserDetails” object. Since I want to generate the Refresh Token, I am passing the second parameter as Refresh Token Type.

 UserDetails userDetails = authenticationService.loadUserByUsername(jwtUtils.extractUsername(refreshToken,TokenType.REFRESH_TOKEN));

Once populating UserDetails object I am validating user details using below Spring security framework API.

try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword()));
} catch (BadCredentialsException bce) {
throw new InvalidUserNamePasswordException(bce.getMessage());
}

If the credentials are not valid, it returns BadCredentialException otherwise execute the following code.

AuthenticationResponseDTO authenticationResponseDTO = authenticationService.generateAuthResponse(userDetails, TokenType.REFRESH_TOKEN);
JwtToken jwtTokenResponse = authenticationService.saveJwt(authenticationResponseDTO);

The following code already explained part of the authenticate web service endpoint. In this case, I am passing Token Type as a Refresh Token, it will generate a Refresh Token. In order to generate Refresh Token following code section execute in generateAuthResponse

if(tokenType.equals(TokenType.REFRESH_TOKEN)) {
    String accessToken = jwtUtils.generateAccessToken(userDetails);
    final Claims accessclaims = jwtUtils.extractAllClaims(accessToken);
    authenticationResponseDTO = new AuthenticationResponseDTO();
    authenticationResponseDTO.setAccessToken(accessToken);
    authenticationResponseDTO.setRefreshToken(jwtToken.getRefreshToken());
    authenticationResponseDTO.setUserName(userDetails.getUsername());
    authenticationResponseDTO.setAccessTokenExpiration(Utils.convertToLocalDateTime(accessclaims.getExpiration().getTime()));
    authenticationResponseDTO.setRefreshTokenExpiration(jwtToken.getRefreshTokenExpiration());
    authenticationResponseDTO.setId(jwtToken.getId());
}

Please watch the below video for the JWT Access token and Refresh token full demo. This video is only for demo and JWT flow explanation (not for coding explanation)
https://youtu.be/8bdg-F9AHdM
You can download the latest code from the following GitHub repository.
https://github.com/sureshstalin/billingsystem


Please follow our ITGARDEN YouTube channel for Billing System Project development work using Spring Boot. The Project source code absolutely free. This project is very good for who is looking for real-time project experience in Spring Boot. To understand the Billing System Project code, watch all Billing System-related YouTube videos from this link. Click Here
Our YouTube Channel
https://www.youtube.com/channel/UChj5CeuWaHIFr4RkXoG3iJA?view_as=subscriber
Please follow me on Medium.com for all billing system related stories and Java-based technical Articles
https://suresh-stalin.medium.com/
Note: To find all billing System-related stories find “billing system itgarden” in medium.com

Close Menu
×
×

Cart