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




















































Spring Boot + JSON Web Token (JWT) Refresh Token(2021) Example

In this tutorial we will be implementing a JWT Refresh Token and its working with the help of a simple example
We have already understood the working of JWT in our previous article.
The access token contains all the information of the server in an encrypted form. Generally in an enterprise application, Ensuring that the security of the application is preserved, there is an expiration time after which the access token is not in any use.
So now the user need to validate himself again. But By using the refresh token, the user can directly get the new access token which will help user to fetch all the API's for which he is authorised. This can be diagrammatically explained as follows-

Introduction

Table Of Contents :


  • This will be the standard directory layout for maven project structure-
    Spring Boot Maven 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.11.RELEASE</version>
    			<relativePath/> <!-- lookup parent from repository -->
    		</parent>
    		<groupId>com.codeusingjava</groupId>
    		<artifactId>JwtRefreshToken</artifactId>
    		<version>0.0.1-SNAPSHOT</version>
    		<name>spring-boot-security-jwt</name>
    		<description>Spring Boot JWT Authentication with Refresh Token example</description>
    	
    		<properties>
    			<java.version>1.8</java.version>
    		</properties>
    	
    		<dependencies>
    			<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>mysql</groupId>
    				<artifactId>mysql-connector-java</artifactId>
    				<scope>runtime</scope>
    			</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>
    			</dependency>
    			
    			<dependency>
    				<groupId>org.springframework.security</groupId>
    				<artifactId>spring-security-test</artifactId>
    				<scope>test</scope>
    			</dependency>
    		</dependencies>
    	
    		<build>
    			<plugins>
    				<plugin>
    					<groupId>org.springframework.boot</groupId>
    					<artifactId>spring-boot-maven-plugin</artifactId>
    				</plugin>
    			</plugins>
    		</build>
    	
    	</project>
    	
    
    The application.properties file is as follows-
    All the configuration regarding jwtSecret and RefreshToken properties are done in this file.
    spring.datasource.url= jdbc:mysql://localhost:3300/jwtspring?useSSL=false
    spring.datasource.username= root
    spring.datasource.password= aptech
    
    spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5InnoDBDialect
    spring.jpa.hibernate.ddl-auto= update
    
    codeusingjava.jwtSecret= codeusingjavaSecret
    codeusingjava.jwtExpirationMs= 90000
    codeusingjava.jwtRefreshExpirationMs= 150000
    
    Create the User model class with the jpa annotations -
    Thee relation between user and role is defined as many to many type.
    package com.codeusingjava.models;
    
    	import java.util.HashSet;
    	import java.util.Set;
    	
    	import javax.persistence.*;
    	import javax.validation.constraints.Email;
    	import javax.validation.constraints.NotBlank;
    	import javax.validation.constraints.Size;
    	
    	@Entity
    	@Table(	name = "users", 
    			uniqueConstraints = { 
    				@UniqueConstraint(columnNames = "username"),
    				@UniqueConstraint(columnNames = "email") 
    			})
    	public class User {
    		@Id
    		@GeneratedValue(strategy = GenerationType.IDENTITY)
    		private Long id;
    	
    		@NotBlank
    		private String username;
    	
    		@NotBlank
    		@Email
    		private String email;
    	
    		@NotBlank
    		@Size(max = 120)
    		private String password;
    		
    		@ManyToMany(fetch = FetchType.LAZY)
    		@JoinTable(	name = "user_roles", 
    					joinColumns = @JoinColumn(name = "user_id"), 
    					inverseJoinColumns = @JoinColumn(name = "role_id"))
    		private Set<Role> roles = new HashSet<>();
    	
    		public User() {
    		}
    	
    		public User(String username, String email, String password) {
    			this.username = username;
    			this.email = email;
    			this.password = password;
    		}
    	
    		public Long getId() {
    			return id;
    		}
    	
    		public void setId(Long id) {
    			this.id = id;
    		}
    	
    		public String getUsername() {
    			return username;
    		}
    	
    		public void setUsername(String username) {
    			this.username = username;
    		}
    	
    		public String getEmail() {
    			return email;
    		}
    	
    		public void setEmail(String email) {
    			this.email = email;
    		}
    	
    		public String getPassword() {
    			return password;
    		}
    	
    		public void setPassword(String password) {
    			this.password = password;
    		}
    	
    		public Set<Role> getRoles() {
    			return roles;
    		}
    	
    		public void setRoles(Set<Role> roles) {
    			this.roles = roles;
    		}
    	}
    	
    
    Creating the Role class with the JPA annotations-
    package com.codeusingjava.models;
    
    import javax.persistence.*;
    
    @Entity
    @Table(name = "roles")
    public class Role {
    	@Id
    	@GeneratedValue(strategy = GenerationType.IDENTITY)
    	private Integer id;
    
    	@Enumerated(EnumType.STRING)
    	@Column(length = 20)
    	private ERole name;
    
    	public Role() {
    
    	}
    
    	public Role(ERole name) {
    		this.name = name;
    	}
    
    	public Integer getId() {
    		return id;
    	}
    
    	public void setId(Integer id) {
    		this.id = id;
    	}
    
    	public ERole getName() {
    		return name;
    	}
    
    	public void setName(ERole name) {
    		this.name = name;
    	}
    }
    
    Creating the enum ERole
    The logged in person will have either the user role or the Admin role.
    package com.codeusingjava.models;
    public enum ERole {
      ROLE_USER,
      ROLE_ADMIN
    }
    
    Creating the Refreshtoken class and defining its properties-
    package com.codeusingjava.models;
    
    import java.time.Instant;
    
    import javax.persistence.*;
    
    @Entity(name = "refreshtoken")
    public class RefreshToken {
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      private long id;
    
      @OneToOne
      @JoinColumn(name = "user_id", referencedColumnName = "id")
      private User user;
    
      @Column(nullable = false, unique = true)
      private String token;
    
      @Column(nullable = false)
      private Instant expiryDate;
    
      public RefreshToken() {
      }
    
      public long getId() {
        return id;
      }
    
      public void setId(long id) {
        this.id = id;
      }
    
      public User getUser() {
        return user;
      }
    
      public void setUser(User user) {
        this.user = user;
      }
    
      public String getToken() {
        return token;
      }
    
      public void setToken(String token) {
        this.token = token;
      }
    
      public Instant getExpiryDate() {
        return expiryDate;
      }
    
      public void setExpiryDate(Instant expiryDate) {
        this.expiryDate = expiryDate;
      }
    
    }
    
    
    Creating the Repository class for the User-
    package com.codeusingjava.repo;
    
    import java.util.Optional;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    
    import com.codeusingjava.models.User;
    
    @Repository
    public interface UserRepository extends JpaRepository<User, Long> {
      Optional<User> findByUsername(String username);
    
      Boolean existsByUsername(String username);
    
      Boolean existsByEmail(String email);
    }
    
    
    Creating the Repository class for the RefreshToken-
    package com.codeusingjava.repo;
    
    import java.util.Optional;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.Modifying;
    import org.springframework.stereotype.Repository;
    
    import com.codeusingjava.models.RefreshToken;
    import com.codeusingjava.models.User;
    
    @Repository
    public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
      Optional<RefreshToken> findByToken(String token);
    
      @Modifying
      int deleteByUser(User user);
    }
    
    
    Creating the repository class for the Role.
    package com.codeusingjava.repo;
    
    import java.util.Optional;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    
    import com.codeusingjava.models.ERole;
    import com.codeusingjava.models.Role;
    
    @Repository
    public interface RoleRepository extends JpaRepository<Role, Long> {
      Optional<Role> findByName(ERole name);
    }
    
    
    Creating the AuthenticationEntryPoint class under the utils package-
    The commence method in the AuthenticationEntryPoint is used for initiating the flow of Authentication.
    package com.codeusingjava.utils;
    
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    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.http.MediaType;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.AuthenticationEntryPoint;
    import org.springframework.stereotype.Component;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    @Component
    public class AuthenticationEntryPointJwt implements AuthenticationEntryPoint {
    
      private static final Logger logger = LoggerFactory.getLogger(AuthenticationEntryPointJwt.class);
    
      @Override
      public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
          throws IOException, ServletException {
        logger.error("Unauthorized error: {}", authException.getMessage());
    
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    
        final Map<String, Object> body = new HashMap<>();
        body.put("status", HttpServletResponse.SC_UNAUTHORIZED);
        body.put("error", "Unauthorized");
        body.put("message", authException.getMessage());
        body.put("path", request.getServletPath());
    
        final ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(response.getOutputStream(), body);
    
      }
    
    }
    
    
    Creating the AuthenticationTokenFilter class under the util package- It is the first step where the user will be filtered based on the Authentication Token and its properties. In case if the user has not given the Authorization header, then the request would be rejected.
    package com.codeusingjava.utils;
    
    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.util.StringUtils;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import com.codeusingjava.services.UserDetailsServiceImpl;
    
    public class AuthenticationTokenFilter extends OncePerRequestFilter {
      @Autowired
      private JwtUtils jwtUtils;
    
      @Autowired
      private UserDetailsServiceImpl userDetailsService;
    
      private static final Logger logger = LoggerFactory.getLogger(AuthenticationTokenFilter.class);
    
      @Override
      protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
          throws ServletException, IOException {
        try {
          String jwt = parseJwt(request);
          if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
            String username = jwtUtils.getUserNameFromJwtToken(jwt);
    
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null,
                userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
    
            SecurityContextHolder.getContext().setAuthentication(authentication);
          }
        } catch (Exception e) {
          logger.error("Cannot set user authentication: {}", e.getMessage());
        }
    
        filterChain.doFilter(request, response);
      }
    
      private String parseJwt(HttpServletRequest request) {
        String headerAuth = request.getHeader("Authorization");
    
        if (StringUtils.hasText(headerAuth)) {
          return headerAuth.substring(0, headerAuth.length());
        }
    
        return null;
      }
    }
    
    
    Creating the JwtUtils class-
    The jwtUtils class is one of the most important class used for the generation and validation of the jwt token. Loggers are used for printing the logs to the console.
    package com.codeusingjava.utils;
    
    import java.util.Date;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    import com.codeusingjava.services.UserDetailsImpl;
    
    import io.jsonwebtoken.*;
    
    @Component
    public class JwtUtils {
      private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
    
      @Value("")
      private String jwtSecret;
    
      @Value("")
      private int jwtExpirationMs;
    
      public String generateJwtToken(UserDetailsImpl userPrincipal) {
        return generateTokenFromUsername(userPrincipal.getUsername());
      }
    
      public String generateTokenFromUsername(String username) {
        return Jwts.builder().setSubject(username).setIssuedAt(new Date())
            .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)).signWith(SignatureAlgorithm.HS512, jwtSecret)
            .compact();
      }
    
      public String getUserNameFromJwtToken(String token) {
        return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();
      }
    
      public boolean validateJwtToken(String authToken) {
        try {
          Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
          return true;
        } catch (SignatureException e) {
          logger.error("Invalid JWT signature: {}", e.getMessage());
        } catch (MalformedJwtException e) {
          logger.error("Invalid JWT token: {}", e.getMessage());
        } catch (ExpiredJwtException e) {
          logger.error("JWT token is expired: {}", e.getMessage());
        } catch (UnsupportedJwtException e) {
          logger.error("JWT token is unsupported: {}", e.getMessage());
        } catch (IllegalArgumentException e) {
          logger.error("JWT claims string is empty: {}", e.getMessage());
        }
    
        return false;
      }
    
    }
    
    
    Creating the LoginRequest under the jwtrequest package-
    Here the parameters that are required while login are mentioned.
    package com.codeusingjava.jwtrequest;
    
    import javax.validation.constraints.NotBlank;
    
    public class LoginRequest {
    	@NotBlank
    	private String username;
    
    	@NotBlank
    	private String 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;
    	}
    }
    
    
    Creating the Signup Request class- Here the parameters that are needed while registering are mentioned.
    package com.codeusingjava.jwtrequest;
    
    import java.util.Set;
    
    import javax.validation.constraints.*;
     
    public class SignupRequest {
        @NotBlank
        @Size(min = 3, max = 20)
        private String username;
     
        @NotBlank
        @Size(max = 50)
        @Email
        private String email;
        
        private Set<String> role;
        
        @NotBlank
        @Size(min = 6, max = 40)
        private String password;
      
        public String getUsername() {
            return username;
        }
     
        public void setUsername(String username) {
            this.username = username;
        }
     
        public String getEmail() {
            return email;
        }
     
        public void setEmail(String email) {
            this.email = email;
        }
     
        public String getPassword() {
            return password;
        }
     
        public void setPassword(String password) {
            this.password = password;
        }
        
        public Set<String> getRole() {
          return this.role;
        }
        
        public void setRole(Set<String> role) {
          this.role = role;
        }
    }
    
    
    Creating the RefreshTokenRequest class.
    Here the parameter i.e refreshtoken is mentioned which is needed to fetch the following api.
    package com.codeusingjava.jwtrequest;
    
    import javax.validation.constraints.NotBlank;
    
    public class RefreshTokenRequest {
      @NotBlank
      private String refreshToken;
    
      public String getRefreshToken() {
        return refreshToken;
      }
    
      public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
      }
    }
    
    
    Creating the Signupresponse class.
    It contains the parameters that are coming in the jwtresponse after successful execution of the API.
    package com.codeusingjava.jwtresponse;
    
    import java.util.List;
    
    public class SignupResponse {
    	private String token;
    	private String type = "Bearer";
    	private String refreshToken;
    	private Long id;
    	private String username;
    	private String email;
    	private List<String> roles;
    
    	public SignupResponse(String accessToken, String refreshToken, Long id, String username, String email, List roles) {
    		this.token = accessToken;
    		this.refreshToken = refreshToken;
    		this.id = id;
    		this.username = username;
    		this.email = email;
    		this.roles = roles;
    	}
    
    	public String getAccessToken() {
    		return token;
    	}
    
    	public void setAccessToken(String accessToken) {
    		this.token = accessToken;
    	}
    
    	public String getTokenType() {
    		return type;
    	}
    
    	public void setTokenType(String tokenType) {
    		this.type = tokenType;
    	}
    
    	public Long getId() {
    		return id;
    	}
    
    	public void setId(Long id) {
    		this.id = id;
    	}
    
    	public String getEmail() {
    		return email;
    	}
    
    	public void setEmail(String email) {
    		this.email = email;
    	}
    
    	public String getUsername() {
    		return username;
    	}
    
    	public void setUsername(String username) {
    		this.username = username;
    	}
    
    	public List<String> getRoles() {
    		return roles;
    	}
    
      public String getRefreshToken() {
        return refreshToken;
      }
    
      public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
      }
    }
    
    
    Message Response will be executed when the user will fetch any api for which he is authorised.
     
    package com.codeusingjava.jwtresponse;
    
    public class MessageResponse {
    	private String message;
    
    	public MessageResponse(String message) {
    	    this.message = message;
    	  }
    
    	public String getMessage() {
    		return message;
    	}
    
    	public void setMessage(String message) {
    		this.message = message;
    	}
    }
    
    
    After successful execution of RefreshTokenAPI, the response is coming in the form of TokenRefreshResponse.
      
    package com.codeusingjava.jwtresponse;
    
    public class TokenRefreshResponse {
      private String accessToken;
      private String refreshToken;
      private String tokenType = "Bearer";
    
      public TokenRefreshResponse(String accessToken, String refreshToken) {
        this.accessToken = accessToken;
        this.refreshToken = refreshToken;
      }
    
      public String getAccessToken() {
        return accessToken;
      }
    
      public void setAccessToken(String token) {
        this.accessToken = token;
      }
    
      public String getRefreshToken() {
        return refreshToken;
      }
    
      public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
      }
    
      public String getTokenType() {
        return tokenType;
      }
    
      public void setTokenType(String tokenType) {
        this.tokenType = tokenType;
      }
    
    }
    
    
    TokenRefreshException class will come into existence in case any exception occurs while refreshing the token.
    package com.codeusingjava.exception;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.ResponseStatus;
    
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public class TokenRefreshException extends RuntimeException {
    
      private static final long serialVersionUID = 1L;
    
      public TokenRefreshException(String token, String message) {
        super(String.format("Failed for [%s]: %s", token, message));
      }
    }
    
    
    The refreshTokenService class contains the methods which are necessary for creating and checking the expiration of the token as well as verification of the refreshToken.
    package com.codeusingjava.services;
    
    import java.time.Instant;
    import java.util.Optional;
    import java.util.UUID;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.codeusingjava.exception.TokenRefreshException;
    import com.codeusingjava.models.RefreshToken;
    import com.codeusingjava.repo.RefreshTokenRepository;
    import com.codeusingjava.repo.UserRepository;
    
    @Service
    public class RefreshTokenService {
      @Value("")
      private Long refreshTokenDurationMs;
    
      @Autowired
      private RefreshTokenRepository refreshTokenRepository;
    
      @Autowired
      private UserRepository userRepository;
    
      public Optional<RefreshToken> findByToken(String token) {
        return refreshTokenRepository.findByToken(token);
      }
    
      public RefreshToken createRefreshToken(Long userId) {
        RefreshToken refreshToken = new RefreshToken();
    
        refreshToken.setUser(userRepository.findById(userId).get());
        refreshToken.setExpiryDate(Instant.now().plusMillis(refreshTokenDurationMs));
        refreshToken.setToken(UUID.randomUUID().toString());
    
        refreshToken = refreshTokenRepository.save(refreshToken);
        return refreshToken;
      }
    
      public RefreshToken verifyExpiration(RefreshToken token) {
        if (token.getExpiryDate().compareTo(Instant.now()) < 0) {
          refreshTokenRepository.delete(token);
          throw new TokenRefreshException(token.getToken(), "Refresh token was expired. Please make a new signin request");
        }
    
        return token;
      }
    
      @Transactional
      public int deleteByUserId(Long userId) {
        return refreshTokenRepository.deleteByUser(userRepository.findById(userId).get());
      }
    }
    
    
    The userDetailsIml class is defined as follows-
    This class contains the methods that are used to fetch all the details of the logged in user.
    package com.codeusingjava.services;
    
    import java.util.Collection;
    import java.util.List;
    import java.util.Objects;
    import java.util.stream.Collectors;
    
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    
    import com.codeusingjava.models.User;
    import com.fasterxml.jackson.annotation.JsonIgnore;
    
    public class UserDetailsImpl implements UserDetails {
    	private static final long serialVersionUID = 1L;
    
    	private Long id;
    
    	private String username;
    
    	private String email;
    
    	@JsonIgnore
    	private String password;
    
    	private Collection authorities;
    
    	public UserDetailsImpl(Long id, String username, String email, String password,
    			Collection authorities) {
    		this.id = id;
    		this.username = username;
    		this.email = email;
    		this.password = password;
    		this.authorities = authorities;
    	}
    
    	public static UserDetailsImpl build(User user) {
    		List authorities = user.getRoles().stream()
    				.map(role -> new SimpleGrantedAuthority(role.getName().name()))
    				.collect(Collectors.toList());
    
    		return new UserDetailsImpl(
    				user.getId(), 
    				user.getUsername(), 
    				user.getEmail(),
    				user.getPassword(), 
    				authorities);
    	}
    
    	@Override
    	public Collection getAuthorities() {
    		return authorities;
    	}
    
    	public Long getId() {
    		return id;
    	}
    
    	public String getEmail() {
    		return email;
    	}
    
    	@Override
    	public String getPassword() {
    		return password;
    	}
    
    	@Override
    	public String getUsername() {
    		return username;
    	}
    
    	@Override
    	public boolean isAccountNonExpired() {
    		return true;
    	}
    
    	@Override
    	public boolean isAccountNonLocked() {
    		return true;
    	}
    
    	@Override
    	public boolean isCredentialsNonExpired() {
    		return true;
    	}
    
    	@Override
    	public boolean isEnabled() {
    		return true;
    	}
    
    	@Override
    	public boolean equals(Object o) {
    		if (this == o)
    			return true;
    		if (o == null || getClass() != o.getClass())
    			return false;
    		UserDetailsImpl user = (UserDetailsImpl) o;
    		return Objects.equals(id, user.id);
    	}
    }
    
    
    The userDetailsServiceIml class is defined as follows-
    This class will load user by taking the username. In case the particular user is not found then UsernameNotFoundException will be thrown.
    package com.codeusingjava.services;
    
    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 org.springframework.transaction.annotation.Transactional;
    
    import com.codeusingjava.models.User;
    import com.codeusingjava.repo.UserRepository;
    
    @Service
    public class UserDetailsServiceImpl implements UserDetailsService {
      @Autowired
      UserRepository userRepository;
    
      @Override
      @Transactional
      public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
            .orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));
    
        return UserDetailsImpl.build(user);
      }
    
    }
    
    
    The configuration class under the WebSecurityConfig class is defined as follows-
    All the configuration related to security are done in this class.
    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.crypto.bcrypt.BCryptPasswordEncoder;
    	import org.springframework.security.crypto.password.PasswordEncoder;
    	import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    	
    	import com.codeusingjava.services.UserDetailsServiceImpl;
    	import com.codeusingjava.utils.AuthenticationEntryPointJwt;
    	import com.codeusingjava.utils.AuthenticationTokenFilter;
    	import com.codeusingjava.utils.AuthenticationEntryPointJwt;
    	import com.codeusingjava.utils.AuthenticationTokenFilter;
    	
    	@Configuration
    	@EnableWebSecurity
    	@EnableGlobalMethodSecurity(
    			prePostEnabled = true)
    	public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    		@Autowired
    		UserDetailsServiceImpl userDetailsService;
    	
    		@Autowired
    		private AuthenticationEntryPointJwt unauthorizedHandler;
    	
    		@Bean
    		public AuthenticationTokenFilter authenticationJwtTokenFilter() {
    			return new AuthenticationTokenFilter();
    		}
    	
    		@Override
    		public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
    			authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    		}
    	
    		@Bean
    		@Override
    		public AuthenticationManager authenticationManagerBean() throws Exception {
    			return super.authenticationManagerBean();
    		}
    	
    		@Bean
    		public PasswordEncoder passwordEncoder() {
    			return new BCryptPasswordEncoder();
    		}
    	
    		@Override
    		protected void configure(HttpSecurity http) throws Exception {
    			http.cors().and().csrf().disable()
    				.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
    				.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
    				.authorizeRequests().antMatchers("/app/**").permitAll()
    				.antMatchers("/app/test/**").permitAll()
    				.anyRequest().authenticated();
    	
    			http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    		}
    	}
    	
    
    The controller class is defined as follows- This class contains the api related to login, register , refreshToken etc..
     
    package com.codeusingjava.controllers;
    
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    import javax.validation.Valid;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.ResponseEntity;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.codeusingjava.exception.TokenRefreshException;
    import com.codeusingjava.jwtrequest.LoginRequest;
    import com.codeusingjava.jwtrequest.SignupRequest;
    import com.codeusingjava.jwtrequest.RefreshTokenRequest;
    import com.codeusingjava.jwtresponse.SignupResponse;
    import com.codeusingjava.jwtresponse.MessageResponse;
    import com.codeusingjava.jwtresponse.TokenRefreshResponse;
    import com.codeusingjava.models.ERole;
    import com.codeusingjava.models.RefreshToken;
    import com.codeusingjava.models.Role;
    import com.codeusingjava.models.User;
    import com.codeusingjava.repo.RoleRepository;
    import com.codeusingjava.repo.UserRepository;
    import com.codeusingjava.services.RefreshTokenService;
    import com.codeusingjava.services.UserDetailsImpl;
    import com.codeusingjava.utils.JwtUtils;
    
    @CrossOrigin(origins = "*", maxAge = 3600)
    @RestController
    @RequestMapping("/app")
    public class AuthController {
      @Autowired
      AuthenticationManager authenticationManager;
    
      @Autowired
      UserRepository userRepository;
    
      @Autowired
      RoleRepository roleRepository;
    
      @Autowired
      PasswordEncoder encoder;
    
      @Autowired
      JwtUtils jwtUtils;
    
      @Autowired
      RefreshTokenService refreshTokenService;
    
      @PostMapping("/login")
      public ResponseEntity authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
    
        Authentication authentication = authenticationManager
            .authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
    
        SecurityContextHolder.getContext().setAuthentication(authentication);
    
        UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
    
        String jwt = jwtUtils.generateJwtToken(userDetails);
    
        List<String> roles = userDetails.getAuthorities().stream().map(item -> item.getAuthority())
            .collect(Collectors.toList());
    
        RefreshToken refreshToken = refreshTokenService.createRefreshToken(userDetails.getId());
    
        return ResponseEntity.ok(new SignupResponse(jwt, refreshToken.getToken(), userDetails.getId(),
            userDetails.getUsername(), userDetails.getEmail(), roles));
      }
    
      @PostMapping("/register")
      public ResponseEntity<?> registerUser(@Valid @RequestBody SignupRequest signUpRequest) {
        if (userRepository.existsByUsername(signUpRequest.getUsername())) {
          return ResponseEntity.badRequest().body(new MessageResponse("Error: Username is already taken!"));
        }
    
        if (userRepository.existsByEmail(signUpRequest.getEmail())) {
          return ResponseEntity.badRequest().body(new MessageResponse("Error: Email is already in use!"));
        }
    
        User user = new User(signUpRequest.getUsername(), signUpRequest.getEmail(),
            encoder.encode(signUpRequest.getPassword()));
    
        Set<String> strRoles = signUpRequest.getRole();
        Set<Role> roles = new HashSet<>();
    
        if (strRoles == null) {
          Role userRole = roleRepository.findByName(ERole.ROLE_USER)
              .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
          roles.add(userRole);
        } else {
          strRoles.forEach(role -> {
            switch (role) {
            case "admin":
              Role adminRole = roleRepository.findByName(ERole.ROLE_ADMIN)
                  .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
              roles.add(adminRole);
    
              break;
    
            default:
              Role userRole = roleRepository.findByName(ERole.ROLE_USER)
                  .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
              roles.add(userRole);
            }
          });
        }
    
        user.setRoles(roles);
        userRepository.save(user);
    
        return ResponseEntity.ok(new MessageResponse("User registered successfully!"));
      }
    
      @PostMapping("/refreshtoken")
      public ResponseEntity<?> refreshtoken(@Valid @RequestBody RefreshTokenRequest request) {
        String requestRefreshToken = request.getRefreshToken();
    
        return refreshTokenService.findByToken(requestRefreshToken)
            .map(refreshTokenService::verifyExpiration)
            .map(RefreshToken::getUser)
            .map(user -> {
              String token = jwtUtils.generateTokenFromUsername(user.getUsername());
              return ResponseEntity.ok(new TokenRefreshResponse(token, requestRefreshToken));
            })
            .orElseThrow(() -> new TokenRefreshException(requestRefreshToken,
                "Refresh token is not in database!"));
      }
      
    
    
    }
    
    The TestController contains the api that are based on the role.
    In case a person tries to access the Request for which he is not authenticated, an exception is thrown and the logged in user is forbidden.
    package com.codeusingjava.controllers;
    
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/app/test")
    public class TestController {
    	
    	@GetMapping("/user")
    	@PreAuthorize("hasRole('USER')  or hasRole('ADMIN')")
    	public String userAccess() {
    		return "User Here.";
    	}
    
    
    
    	@GetMapping("/admin")
    	@PreAuthorize("hasRole('ADMIN')")
    	public String adminAccess() {
    		return "Admin Here.";
    	}
    }
    
    
    The main class for the JwtRefreshTokenApplication is defined as follows-
    package com.codeusingjava;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class JwtRefreshTokenApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(JwtRefreshTokenApplication.class, args);
    	}
    
    }
    
    
If we now run the application we get the output as follows-
We Run the application as Spring boot application.
register
Now we run the login api.After successful execution, we see that a accessToken and refreshToken are in the output response.
register

Here in our example, the role of the logged in user is user. So he is only able to access user api using the access token.
register

User content fetched successfully.
register
If we try after a long time (here 9 sec.) from the access token,
register
User can generate new jwt token using refreshtoken.
register
In case the refreshtoken gets expired.
register

Login with a new token generated.
register

Downloads-

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