sarala.jamadar@gmail.com +46 760221684

Indroduction of tokens in Microservices

A microservice can have two kinds of endpoints.

  1. Public endpoints: Which is meant to be accessed by everyone without a need to authenticate an user. (e.g: You can search for a user and get basic details like Name and Address)
  2. Restricted Endpoints: These are only accessible by authenticated users who has certain permissions. (e.g: Not all can view account balance of a user. For this you need to be authenticated first)

One way to secure our endpoint access can be to implement password authentication. In this model an user sends username and password with the request and the microservice can authenticate and authorize this request and send response. This model is fine but it has a big flaw, i.e. the passwords are valid for a long time. If a hacker gets your password then he will have access to your services for a very long time until you change your passwords. And also its very probable that a person keeps changing his password in a pattern, I mean he/she just changes few characters in current password and updates. So put in place a hacker can try his luck by guessing your new password and once he knows the pattern he will be able to access your services for ever.

But don't be scared, as you know Software Engineers are always smart and they have solutions for every problem :-) To solve the above scenario token based system access is very conventional. It works like this : If you want to access some secured service then you will have to authenticate yourself with a server (Also called as Authentication Services) and these services will validate your credentials and then will generate a short lived access token. With this access token you will make a call to the secured services and they will validate your access token and serve your request.

Usually, authentication services keep the access token validity for few minutes. Hence even if any hacker gets your accesstoken he won't be able to use it much.

JWT Token

Below section describes how JWT tokens are used in microservices

JWT Token Issue and Validation

JWT (JSON Web Token) is a very popular access token issuance and validation standard that is used in microservices world. JWT tokens are Base64 encoded JSON string which contains a Header(Information on encryption algorythm) , a Payload(Information on authorization and its expiry) and a Signature signed with HMAC or RSA

In the picture you can see we have an User which is trying to access Account Services. For this, User has to first get access token from Authentication Services and then make a call to account services with payload and access token.

Code

Below is an end to end example of creating an Authentication Service that issues access token and creating a secure microservice endpoint that valides the token and sends response

Create Authentication Server

Here we will create an endpoint called "/api/v1/jwt/token" which will authenticate user request and issue JWT access token.

  1. Go to url: https://start.spring.io/
  2. Choose dependencies as Spring Web.
  3. Click on 'Generate button' to download a zip file. Unzip the file.
  4. Import this folder into IntelliJ or Eclipse as a maven project.
  5. Open pom.xml file and add below dependencies: <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.1</version> </dependency>
  6. Annotate your main class with @SpringBootApplication
  7. Create two packages named controllers and services under package com.coursemicron.jwttokenproviderservice

  8. Now lets create our interface JwtTokenProviderService in service package which will have method to generate token
    public interface JwtTokenProviderService { Map<String, String> generateToken(String userName); }
  9. In service package now you create a class named JwtTokenProviderServiceImpl which implements JwtTokenProviderService. Below is the implementation of generateToken method
    public class JwtTokenProviderServiceImpl implements JwtTokenProviderService{ @Value("${jwt.secret}") private String secret; @Value("${app.jwttoken.message}") private String message; /** * @param userName * @return */ @Override public Map<String, String> generateToken(String userName) { String jwtToken=""; Date currentDate = new Date(); long t = currentDate.getTime(); Date expirationTime = new Date(t + 300000l); // 5 minutes jwtToken = Jwts.builder().setSubject(userName).setIssuedAt(currentDate).setExpiration(expirationTime).signWith(SignatureAlgorithm.HS256, secret).compact(); Map<String, String> jwtTokenGen = new HashMap<>(); jwtTokenGen.put("token", jwtToken); jwtTokenGen.put("message", message); return jwtTokenGen; } } Explanation of above code:
    secret : is injected from property file and this one is a application secret key normally a SHA string. This is string is signed and then the signature is varified by the secured microservices endpoints.
    message : is alo injected from property file. Normally its just some text saying "Authenticated successfully!"
    currentDate : this is current date and time
    expirationTime : set to 5 minutes from current date time. So that our access token is valid for 5 minutes only.
    jwtToken : Is generated by Jwts class where it needs a subject (typically username), setIssuesAt (current date), setExpiration(setting expiration time), signWith(sign your application secret key with HS256 algorythm), compact(returns access token as String value)
    jwtTokenGen : This is a map of values like token, message. This is also used in secured microservice endpoint to fetch the values and validate them.
  10. As secret and message are configurable we will keep them in application.properties file like below: jwt.secret=appsecretkey app.jwttoken.message=Authenticated successfully!
  11. Now lets create our microservice endpoint that validates username and password provided in payload and then generated a token and responds as JSON. For this we will create a controller class in com.coursemicron.jwttokenproviderservice.controllers package @RestController @RequestMapping("api/v1/jwt") public class JwtTokenProviderServiceController { @Autowired JwtTokenProviderService jwtTokenProviderService; @PostMapping("/token") public ResponseEntity<?> authenticate(@RequestHeader("username") String userName, @RequestHeader("secret") String secret){ if(userName.equals("admin") && secret.equals("Pwd@123")){ return new ResponseEntity<>(jwtTokenProviderService.generateToken(userName), HttpStatus.OK); } return new ResponseEntity<>("Failed authenticate user.", HttpStatus.CONFLICT); } }
  12. Perform maven clean install with command : mvn clean install
  13. Run the project with IDE or with command : java -jar 'JAR_FILE_NAME'
  14. Now your authentication server will be up and running and you can use postman to call your api and get access token.

Create a Secure Endpoint which validates JWT token for grsanting access

In this section we will create a microservice with two endpoints, one which is accessible by everyone and second that is secured by JWT token validation step.

  1. Go to url: https://start.spring.io/
  2. Choose dependencies as Spring Web.
  3. Click on 'Generate button' to download a zip file. Unzip the file.
  4. Import this folder into IntelliJ or Eclipse as a maven project.
  5. Open pom.xml file and add below dependencies: <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.1</version> </dependency>
  6. Annotate your main class with @SpringBootApplication
  7. Create three packages named controllers , configuration and filters under package com.coursemicron.jwtenabledsecuremicroservice
  8. Now our main goal is to create a filter that can perfomr token validation on incoming request before reaching the actual endpoint. For that we will create a filter under package com.coursemicron.jwtenabledsecuremicroservice.filters public class JwtFilter extends GenericFilterBean { final String secret = "appsecretkey"; // Should be the same value as used while generating JWT token @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpServletResponse response = (HttpServletResponse) servletResponse; final String authHeader = request.getHeader("authorization"); if ("OPTIONS".equals(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); filterChain.doFilter(request, response); } else { if(authHeader == null || !authHeader.startsWith("Bearer ")){ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Authorization header needed"); return; } } final String token = authHeader.substring(7); try { Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); request.setAttribute("claims", claims); }catch (Exception e){ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid token."); return ; } filterChain.doFilter(request, response); } }
    Explanation:
    Out filter class first checks if user has provided a Beaere token or not. If its missing then it raises error saying 'Authorization header needed'
    If token is found then we extract it from header and store it in token variable.
    Now we have used Jwts.parser() method to parse the token with application secret key. If token is invalid then it will create Exception which we have handled properly and send a 401 error to client saying 'Invalid token.'
  9. Now its time for us to create our endpoints with below controller class under package com.coursemicron.jwtenabledsecuremicroservice.controllers @RestController @RequestMapping("api/v1/service") public class SecureServiceController { @GetMapping("/unrestricted") public ResponseEntity<?> getMessage() { return new ResponseEntity<>("Hello... You are receiving unrestricted data.", HttpStatus.OK); } @GetMapping("/secure") public ResponseEntity<?> getRestrictedMessage() { return new ResponseEntity<>("Cool , you have now received secure data.", HttpStatus.OK); } } Here we have created two endpoints "api/v1/service/unrestricted" and "api/v1/service/secure"
  10. One last final puzzle we still need to solve. That is how to mapply our filter on this endpoint "api/v1/service/secure" or any endpoint with a pattern "api/v1/service/secure/*" ?
    For this we will have to create a configuration class under package com.coursemicron.jwtenabledsecuremicroservice.configuration which will do the trick. @Configuration public class ServiceFilterConfiguration { @Bean public FilterRegistrationBean jwtFilter() { FilterRegistrationBean filter= new FilterRegistrationBean(); filter.setFilter(new JwtFilter()); // provide endpoints which needs to be restricted. // All Endpoints would be restricted if unspecified filter.addUrlPatterns("/api/v1/service/secure"); return filter; } }
  11. That's it !! we are done. Now you can build and run the project like below
  12. Perform maven clean install with command : mvn clean install
  13. Run the project with IDE or with command : java -jar 'JAR_FILE_NAME'
  14. Your microservice will be running and you can test with postman as below.

Summary

Congratulations you have successfully created a simple authentication server and a microservice that has secure endpoint with JWT.

If you want to download the project please follow below git repositories:

  1. https://github.com/coursemicron/jwt-token-provider-service
  2. https://github.com/coursemicron/jwt-enabled-secure-microservice