Skip to content

pkini2002/JWT-Authentication-SpringBoot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JWT-Authentication using SpringBoot 3.1.4

JWT stands for JSON Web Token. They are a popular way to implement authentication and authorization in web applications.

A JWT is made up of three parts:

  • Header : The header contains information about the token, such as the type of token (JWT) and the signing algorithm used.
  • Payload : The payload is the main body of the token and contains the claims, which are statements about the user or other entity.
  • Signature : The signature is used to verify the token's integrity. It is created by signing the header and payload with a secret or public/private key pair.

When a user logs in to a web application, the server generates a JWT and sends it back to the client. The client can then store the token in local storage or session storage. When the user makes subsequent requests to the server, the client includes the JWT in the request header. The server can then verify the token and authenticate the user.


Architecture of JWT Authentication Flow

Steps to implement the project

Specifications

Type Value
Language Java
Build Automation tool Maven
Spring Boot Version 3.1.4
Packaging JAR
Java Version 17

Dependencies Used

  • Web Dependency

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

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

  • Lombok
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> 

  • JWT Dependencies

 <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred --> <version>0.11.5</version> <scope>runtime</scope> </dependency> 

Steps

  • Create an Endpoint to be secured by creating a controller package in order to create views

@RestController public class HomeController { Logger logger = LoggerFactory.getLogger(HomeController.class); @RequestMapping("/test") public String test() { this.logger.warn("This is working message"); return "Testing message"; } } 

  • Create In-Memory User with UserDetailService Bean

@Configuration class MyConfig { @Bean public UserDetailsService userDetailsService() { UserDetails userDetails = User.builder(). username("DURGESH") .password(passwordEncoder().encode("DURGESH")).roles("ADMIN"). build(); return new InMemoryUserDetailsManager(userDetails); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration builder) throws Exception { return builder.getAuthenticationManager(); } } 

Steps to implement JWT Token


  • Make sure spring-boot-starter-security is there in pom.xml

  • Create Class JWTAthenticationEntryPoint that implements AuthenticationEntryPoint. The method of this class is called whenever an exception is thrown due to an

  • Unauthenticated user trying to access the resource that required authentication.

@Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); PrintWriter writer = response.getWriter(); writer.println("Access Denied !! " + authException.getMessage()); } } 

  • Create the JWTHelper class This class contains a method related to perform operations with jwt tokens like generateToken, validateToken, etc.

@Component public class JwtHelper { //requirement : public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60; // public static final long JWT_TOKEN_VALIDITY = 60; private String secret = "afafasfafafasfasfasfafacasdasfasxASFACASDFACASDFASFASFDAFASFASDAADSCSDFADCVSGCFVADXCcadwavfsfarvf"; //retrieve username from jwt token public String getUsernameFromToken(String token) { return getClaimFromToken(token, Claims::getSubject); } //retrieve expiration date from jwt token 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); } //for retrieveing any information from token we will need the secret key private Claims getAllClaimsFromToken(String token) { return Jwts.parser().setSigningKey(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()); } //generate token for user public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return doGenerateToken(claims, userDetails.getUsername()); } //while creating the token - //1. Define claims of the token, like Issuer, Expiration, Subject, and the ID //2. Sign the JWT using the HS512 algorithm and secret key. //3. According to JWS Compact Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1) // compaction of the JWT to a URL-safe string 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, secret).compact(); } //validate token public Boolean validateToken(String token, UserDetails userDetails) { final String username = getUsernameFromToken(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } } 

  • Create JWTAuthenticationFilter that extends OncePerRequestFilter and override method and write the logic to check the token that is comming in header. We have to write 5 important logic
    • Get Token from request
    • Validate Token
    • GetUsername from token
    • Load user associated with this token
    • Set Authentication

@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { private Logger logger = LoggerFactory.getLogger(OncePerRequestFilter.class); @Autowired private JwtHelper jwtHelper; @Autowired private UserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //Authorization String requestHeader = request.getHeader("Authorization"); //Bearer 2352345235sdfrsfgsdfsdf logger.info(" Header : {}", requestHeader); String username = null; String token = null; if (requestHeader != null && requestHeader.startsWith("Bearer")) { //looking good token = requestHeader.substring(7); try { username = this.jwtHelper.getUsernameFromToken(token); } catch (IllegalArgumentException e) { logger.info("Illegal Argument while fetching the username !!"); e.printStackTrace(); } catch (ExpiredJwtException e) { logger.info("Given jwt token is expired !!"); e.printStackTrace(); } catch (MalformedJwtException e) { logger.info("Some changed has done in token !! Invalid Token"); e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } else { logger.info("Invalid Header Value !! "); } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { //fetch user detail from username UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); Boolean validateToken = this.jwtHelper.validateToken(token, userDetails); if (validateToken) { //set the authentication UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } else { logger.info("Validation fails !!"); } } filterChain.doFilter(request, response); } } 

  • Configure spring security in configuration file:

@Configuration public class SecurityConfig { @Autowired private JWTAthenticationEntryPoint point; @Autowired private JWTAuthenticationFilter filter; public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { // configuration http.csrf(csrf->csrf.disable()) .cors(cors->cors.disable()) .authorizeHttpRequests(auth->auth.requestMatchers("/home/**").authenticated() .requestMatchers("/auth/login").permitAll().anyRequest() .authenticated()) .exceptionHandling(ex->ex.authenticationEntryPoint(point)) .sessionManagement(session->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); http.addFilterBefore(filter,UsernamePasswordAuthenticationFilter.class); return http.build(); } } 

  • Create JWTRequest and JWTResponse in models package to receive request data and send a Login success response.

JWTRequest


@Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder @ToString public class JwtRequest { private String email; private String password; } 

JWTResponse

@Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder @ToString public class JwtResponse { private String jwtToken; private String username; } 

  • Create login api to accept username and password and return token if username and password is correct.

@RestController @RequestMapping("/auth") public class AuthController { @Autowired private UserDetailsService userDetailsService; @Autowired private AuthenticationManager manager; @Autowired private JwtHelper helper; private Logger logger = LoggerFactory.getLogger(AuthController.class); @PostMapping("/login") public ResponseEntity<JwtResponse> login(@RequestBody JwtRequest request) { this.doAuthenticate(request.getEmail(), request.getPassword()); UserDetails userDetails = userDetailsService.loadUserByUsername(request.getEmail()); String token = this.helper.generateToken(userDetails); JwtResponse response = JwtResponse.builder() .jwtToken(token) .username(userDetails.getUsername()).build(); return new ResponseEntity<>(response, HttpStatus.OK); } private void doAuthenticate(String email, String password) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(email, password); try { manager.authenticate(authentication); } catch (BadCredentialsException e) { throw new BadCredentialsException(" Invalid Username or Password !!"); } } @ExceptionHandler(BadCredentialsException.class) public String exceptionHandler() { return "Credentials Invalid !!"; } } 

  • Test the application

About

JWT Authentication API using Spring Boot 3.1.4 (Using Spring Security + Lombok + Spring Web dependencies)

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages