Cyber Guard
Fight against cyber crime

Microservice Authorization by using JWT

Microservice Authorization using JWT

What is Microservice Authorization by using JWT

Microservice is a collection of end points to provide web service. Every end point need to be exposed to outside world. If we want to restrict access of the end point by using authentication then we need to authorize the end points. JWT gives security to the endpoints. The authentication end point gives a token to the user app and subsequent request from users contains the token provided by the authentication endpoint.

What is JWT ?

A JSON web token(JWT) is JSON Object which is used to securely transfer information over the web(between two parties). It can be used for an authentication system and can also be used for information exchange.The token is mainly composed of header, payload, signature. These three parts are separated by dots(.). JWT defines the structure of information we are sending from one party to the another, and it comes in two forms – Serialized, Deserialized. The Serialized approach is mainly used to transfer the data through the network with each request and response. While the deserialized approach is used to read and write data to the web token.

Know more about JWT Here

Requirement to make a simple Spring Boot Microservice with JWT :

Create a Spring boot starter application in eclipse or STS.

Following video will help you to create Spring boot application.

1.Adding Dependencies

Add spring-boot-starter-security and JJWT dependency to the pom.xml

pom.xml

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.javainuse</groupId>
<artifactId>spring-boot-jwt</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
</project>

2.Creating Configuration for Request Filter

jwtRequestFilter.java

package com.jwtTest.config;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import com.jwtTest.service.JwtUserDetailsService;
import io.jsonwebtoken.ExpiredJwtException;
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired private JwtUserDetailsService userDetailsService; 
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {


final String requestTokenHeader = request.getHeader("Authorization");


String username = null;
String jwtToken = null;
// JWT Token is in the form "Bearer token". Remove Bearer word and get only the Token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
System.out.println("Username in token "+username);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}


//Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {


UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);


// if token is valid configure Spring Security to manually set authentication
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {


UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}


}

3. Create a class to create JWT Token

JwtTokenUtil.java

package com.jwtTest.config;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
@Component
public class JwtTokenUtil implements Serializable {


private static final long serialVersionUID = -2550185165626007488L;

public static final long JWT_TOKEN_VALIDITY = 5*60*60;



private String secret="lkjh";


public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}


public Date getIssuedAtDateFromToken(String token) {
return getClaimFromToken(token, Claims::getIssuedAt);
}


public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}


public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}



private Claims getAllClaimsFromToken(String token) {

return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}


private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}


private Boolean ignoreTokenExpiration(String token) {
// here you specify tokens, for that the expiration is ignored
return false;
}


public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, userDetails.getUsername());
}


private String doGenerateToken(Map<String, Object> claims, String subject) {


return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY*1000)).signWith(SignatureAlgorithm.HS512, secret).compact();
}


public Boolean canTokenBeRefreshed(String token) {
return (!isTokenExpired(token) || ignoreTokenExpiration(token));
}


public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}

4. Create a class for web security

WebSecurityConfig.java

package com.jwtTest.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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)


public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private UserDetailsService jwtUserDetailsService;


@Autowired
private JwtRequestFilter jwtRequestFilter;


@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}


@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}


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


@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// We don't need CSRF for this example
httpSecurity.csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/authenticate").permitAll().
// all other requests need to be authenticated
anyRequest().authenticated();



// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}

5. Creating Controller

JwtAuthenticationController.java

package com.jwtTest.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.jwtTest.config.JwtTokenUtil;
@RestController
public class JwtAuthenticationController {


@Autowired
private JwtTokenUtil jwtTokenUtil;


@Autowired
private UserDetailsService userDetailsService;


@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(@RequestParam(value="username") String username,
@RequestParam(value="password") String password)
throws Exception {
if(!username.equals("abcd") || !password.equals("1234"))
  return ResponseEntity.ok("Invalid credential");
   
  final UserDetails userDetails = userDetailsService
.loadUserByUsername(username);
        final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(token);
}
   @RequestMapping({ "/hello" })
public String hello() {
return "Hello JWT";
}
}

6. Creating Service Class

JwtUserDetailsService.java

package com.jwtTest.service;
import java.util.ArrayList;
import org.springframework.security.core.userdetails.User;
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;
@Service
public class JwtUserDetailsService implements UserDetailsService {


@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username.equals("abcd")) {
return new User("abcd", "1234",
new ArrayList<>());
} else {
throw new UsernameNotFoundException("User not found with username: " + username);
}
}


}

Project Explorer View Of STS/Eclipse

Project Explorer View After Completion Of Project

Procedure To Fetch data over Postman with appropriate images

  1. Run the application in Postman and give Request method as “POST” and Request url as “localhost: (port_number)/authenticate”
  2. Click on Body tab and then on form data .
  3. Mention proper key and value to authenticate.
  4. Click on send and get the required “token” .
  5. Now in another tab give Request method as “GET” and Request url as “localhost: (port_number)/hello”.
  6. Click on headers, and in key – type “Authorization” and in value – type bearer (paste the token you got from post method)
  7. Click on Send and Fetch the result.
After Clicking on Send find the “token”
Note: In the given image port number is “8086”
Port number of any web application can be changed by using

server.port=8086 in application.properties file. By default the port is 8080
After Clicking Send fetch the result.
Note: In the given image port number is “8086”

Download full source code for Microservice Authorization by using JWT

Download Zip

Share

You may also like...

Leave a Reply

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