Using JWT to secure RESTful services

We will now see how to secure RESTful services using JWT. Let's take the HR service developed in the previous chapter for this purpose. For illustration purposes, we will be abstracting the authentication as part of HRService. In a real-life scenario, the authentication service will be deployed in a separate server. The authentication function will validate the user credentials and respond back with the JWT token for valid users; otherwise, it sends an unauthorized status in response, as follows:

    /**
* Performs authentication of the user and generates JWT Token
*
* @return JWT Token in the Response
*/
@POST
@Path("/login")
@Consumes(APPLICATION_FORM_URLENCODED)
public Response authenticateUser(@FormParam("login") String login,
@FormParam("password") String password) {
try {
// TO-DO Authenticate the user using the credentials provided
//for brevity authenticaton of credentials is not included.
//Also as best practice authentication service will be hosted on
//dedicated auth server.

// Issue a token for the user
String token = JWTAuthHelper.issueToken(login,uriInfo.getAbsolutePath().toString());
// Return the token in the response HTTP Authorization Header
return Response.ok().header(AUTHORIZATION, "Bearer " + token).build();
} catch (Exception e) {
return Response.status(UNAUTHORIZED).build();
}
}

Once the JWT token is sent to the client, the client uses the token to invoke business functions, such as findDepartment:

        //Step-1: Submit the login credential to fetch the JWT token
String token = hrServiceClient.getToken("xxxxxx", "yyyyyy");

logger.log(Level.INFO, "Obtained Token:{0}", token);

//Step-2: Find the department using the valid token
if (token != null && !"".equals(token.trim())) {
Department dept = hrServiceClient.findDepartMent(Department.class, "10", token);
if(dept != null)
logger.log(Level.INFO, dept.toString());
}

How does the findDepartment function know that the supplied token is valid? This is accomplished using a custom ContainerRequestFilter filter called the JWTAuthValidationFilter class:

@Provider
@JWTTokenRequired
@Priority(Priorities.AUTHENTICATION)
public class JWTAuthValidationFilter implements ContainerRequestFilter {

private static final Logger logger = Logger.getLogger(JWTAuthValidationFilter.class.getName());

@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
//Step-1: Get the HTTP Authorization header from the request
String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
logger.log(Level.INFO, "Authorization Header : {0}", authorizationHeader);

//Step-2: Check if the Authorization header contains the Token
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
logger.log(Level.SEVERE, "Invalid Authorization Header : {0}", authorizationHeader);
throw new NotAuthorizedException("Authorization header is missing");
}

//Step-3: Extract the token from the Authorization header
String token = authorizationHeader.substring("Bearer".length()).trim();

//Step-4: Validate the token. If invalid, set UNAUTHORIZED response status
if(!JWTAuthHelper.isValidToken(token)) {

requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
}
}
}

This class will be bound to JWTTokenRequired so that the JWTTokenRequired annotation can be used relevantly to secure the findDepartment function, as follows:

    /**
* Get the department details of the specified department only
* when valid authentication token was provided.
*
* @return Department
*/
@GET
@Path("departments/{id}")
@Produces(MediaType.APPLICATION_JSON)
@JWTTokenRequired
public Department findDepartment(@PathParam("id") @Min(value = 0, message = "Department Id must be a positive value") Short id, @Context Request request) {

Department department = entityManager.find(Department.class, id);
return department;
}

Let's wrap up the overall interaction with the following sequence diagram to get an end-to-end view:

Now, let's check the program's output by supplying a valid and invalid token while invoking the findDepartment function of HRService. With a valid token, the corresponding department details are fetched, while the invalid token results in an HTTP 401 Unauthorized error message:

Program Output:
com.packtpub.rest.ch6.client.HRServiceJWTAuthClient main
INFO: Obtained Token:Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ4eHh4eHgiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjE1NjQ3L3Jlc3QtY2hhcHRlcjYtand0c2VydmljZS93ZWJyZXNvdXJjZXMvaHIvbG9naW4iLCJpYXQiOjE1MDM3NzIwNjEsImV4cCI6MTUwMzc3Mjk2MX0.9pUgOkC39yzDGmM9FCtB9tmqA1WutbheKCd94fRkpe_ePC2hvZ3DB50bPOvFvg4eac1NYs30pSWLm9pjm-Mv_g
com.packtpub.rest.ch6.client.HRServiceJWTAuthClient main
INFO: Outcome using valid token:Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ4eHh4eHgiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjE1NjQ3L3Jlc3QtY2hhcHRlcjYtand0c2VydmljZS93ZWJyZXNvdXJjZXMvaHIvbG9naW4iLCJpYXQiOjE1MDM3NzIwNjEsImV4cCI6MTUwMzc3Mjk2MX0.9pUgOkC39yzDGmM9FCtB9tmqA1WutbheKCd94fRkpe_ePC2hvZ3DB50bPOvFvg4eac1NYs30pSWLm9pjm-Mv_g
com.packtpub.rest.ch6.client.HRServiceJWTAuthClient main
INFO: com.packtpub.rest.ch4.model.Department[ departmentId=10 ]
com.packtpub.rest.ch6.client.HRServiceJWTAuthClient main
INFO: Outcome using invalid token:t@´—à%O˜v+nî…SYg¯µ$%…9cZ
com.packtpub.rest.ch6.client.HRServiceJWTAuthClient main
SEVERE: HTTP 401 Unauthorized
In the preceding example, the JWT library is used to issue and verify the JWT token. The following maven dependencies must be included to build the project:
<!--Begin : Added for JWT -->
<dependency>
           <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
</dependency>
<dependency>
           <groupId>com.fasterxml.jackson.core</groupId>
           <artifactId>jackson-databind</artifactId>
           <version>2.8.2</version>
</dependency>
<!-- End : Added for JWT -->

The complete source code for this example is available on the Packt website. You can download the example from the Packt website link that we mentioned at the beginning of this book, in the Preface section. In the downloaded source code, see the rest-chapter6-jwt project.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.109.234