JAX-RS Security using JSON Web Tokens (JWT) for Authentication and Authorization

JAX-RS Security using JSON Web Tokens (JWT) for Authentication and Authorization

In this tutorial we will discuss how to secure JAX-RS RESTful web services using JSON Web Tokens Authentication and Authorization. This form of security is used for authenticating a client using a signed token which can be verified by application servers. This token-based form of security is a ideal candidate for Cross-domain (CORS) access and when server-side scalability is a prime motivation factor.

What is a JSON Web Tokens (JWT)?

JSON Web Tokens, JWT for short, are tokens that contain information that is unique to a user but may also contain any additional information that the user may need. In order to ensure the token has not been altered in any way the token contains a digital signature that is cryptographically encrypted using a strong algorithm such as HMAC SHA-256.

JWT IMPORTANT NOTE

JWT tokens by default are only encoded and not encrypted. So it is important not to include sensitive information such as passwords in the tokens. There is another standard called (RFC 7516) JSON Web Encryption (JWE) for doing so. We will cover JWE in another tutorial.

It is recommended to use TLS/SSL when using JWT in order to ensure that man-in-the-middle (MITM) attacks are not possible. If however, you feel that additional level of protection is needed, then the obvious choice isto go to JWE.

Structure of JSON Web Token

A JSON Web Token is structured in three parts: The header, the payload and the signature separated by period character (.).

jwt_structure

JWT Token with base64 Encoding and SHA-256

Below you can see an actual JSON Web Token with the various parts color coded.

eyJraWQiOiIxIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJhdmFsZGVzLmNvbSIsIm
V4cCI6MTQ1MDYyNjEwMSwianRpIjoicWljcmRfQjlTbzlNR0VLWFpWOTlGZyIsImlhdCI6M
TQ1MDYyNTUwMSwibmJmIjoxNDUwNjI1MzgxLCJzdWIiOiJhcGFjaGV1c2VyIiwicm9sZXMi
OlsiY2xpZW50IiwiYWRtaW4iXX0.nO2S-SN1xln9e36uFJeCykb3eWVCgYFU1f70s-asoRy
mdxK6X-C-rDrUKFaZRIb0qfW10eUUy03ek5kJ_PF3LtFh4XorYVPy-0fXfex3XEuaXuA2BB
N-dhrSqW8P4sWg3egRMct169zAvBcm3b_GCripDeFDg1h1RyFPJXp77gtcQBZLmhiKqMyqA
_9ph2PzGHGLn43JDZovhDrXTdE61eoSMZ0YVtvaecUP8hSB2PBjlcp4wPBXs5__WwXcNpDw
NBdHWs8zcatqRGS1W5BY_BAk3PBYKujkVy2XiyajY4KEmnpKYkrCOq4sxDtfjUJQdea-Q9_
h9CYjyGPHXO9pgw

Header

The header contains the key id or jwt type and the hashing algorithm being used in the JSON Web Token.

{
  "kid": "1",
  "alg": "RS256"
}

When the header is base64 encoded, we can see the first part of the JWT.

eyJraWQiOiIxIiwiYWxnIjoiUlMyNTYifQ

Payload

The payload contains the main part of the JWT, called the JWT claims. These claims are broken up into into registered claims, public claims and private claims. Private claims are additional tid-bits of data that are used by producer and consumer and may provide information needed by the application.

Registered Claims

Registered claims are names that have been reserved by JWT core team and have been registered in the IANA JSON Web Token Claims registry at http://www.iana.org/assignments/jwt/jwt.xhtml. None of these claims are mandatory. The intention was for these claims to be used as a starting point when defining your claims in a JSON Web Token.

  • iss: The issuer or application issuing the token
  • sub: The subject of the token. Usually the user-id of the client
  • aud: The audience or recipients of the token
  • exp: The expiration time of the token
  • nbf: The nbt (not before) claim specifies the time before which the token is not yet valid for access
  • iat: The iat (issued at) claim identifies the time the token was issued
  • jti: The jti (JWT id) provides a unique identifier for the token

Public Claims

Public claim names should be written using collision-resistant names. This includes adding your URL or URI in the name. A good example of best practices usage would be the public name of: https://avaldes.com/claims/systemname and made accessible where all interested parties have equal access to the location.

Private Claims

In a closed environment, one is more likely to use private claim names. In this example, the producer and consumer are part of enterprise or company and they agree on the claim names to exchange additional information like user-id, roles, etc.

{
  "iss": "avaldes.com",
  "exp": 1450626101,
  "jti": "qicrd_B9So9MGEKXZV99Fg",
  "iat": 1450625501,
  "nbf": 1450625381,
  "sub": "apacheuser",
  "roles": [
    "client",
    "admin"
  ]
}

When the body is base64 encoded, we can see the second part of the JWT.

eyJpc3MiOiJhdmFsZGVzLmNvbSIsImV4cCI6MTQ1MDYyNjEwMS
wianRpIjoicWljcmRfQjlTbzlNR0VLWFpWOTlGZyIsImlhdCI6MTQ1MD
YyNTUwMSwibmJmIjoxNDUwNjI1MzgxLCJzdWIiOiJhcGFjaGV1c2VyIi
wicm9sZXMiOlsiY2xpZW50IiwiYWRtaW4iXX0

Signature

HMAC-RSA-SHA-256(
  base64encode(header) + "." +
  base64encode(payload),  
  Private Key
)

The Signature, which is the third part of the JSON Web Token is composed of a base64 encoded header and payload. We take these two parts and make a hash using some type of Message Authentication Code (MAC), in our case, we are using HMAC RSA SHA-256 with the private key or secret to encrypt and seal the integrity of the message in order to ensure it has not been tampered with.

The output of this step, yields the final part of the token.

nO2S-SN1xln9e36uFJeCykb3eWVCgYFU1f70s-asoRymdxK6X
-C-rDrUKFaZRIb0qfW10eUUy03ek5kJ_PF3LtFh4XorYVPy-0fXfex3
XEuaXuA2BBN-dhrSqW8P4sWg3egRMct169zAvBcm3b_GCripDeFDg1h
1RyFPJXp77gtcQBZLmhiKqMyqA_9ph2PzGHGLn43JDZovhDrXTdE61e
oSMZ0YVtvaecUP8hSB2PBjlcp4wPBXs5__WwXcNpDwNBdHWs8zcatqR
GS1W5BY_BAk3PBYKujkVy2XiyajY4KEmnpKYkrCOq4sxDtfjUJQdea-
Q9_h9CYjyGPHXO9pgw

JWT Token Verification Online Debugging Tool

If you would like to verify this token online, JWT.io provides a great debugging tool for all to use.

jwt_online_verify

Benefits of JSON Web Tokens

  • Stateless/Self-Contained: JSON Web Tokens contain all the information necessary to within the token itself to maintain state.
  • Cross-domain/CORS: Since JWTs contain all the information necessary in the token itself they can easily be passed around between domains and between servers.
  • Secure: Since the JWT is passed in the authorization header with every request and validated by the server Cross-Site Request Forgery is prevented
  • Multi-Platform/Multi-Language Support: JWTs are fully supported in .NET, Python, Node.js, Java, Javascript, PHP, Perl, Ruby, Elixir, Go, Haskell, Rust, Lua, Scala, D, Closure, Swift, C and Delphi

Getting Started

In order to run this tutorial yourself, you will need the following:

Required Libraries

Copy all of the following jars to WebContent->WEB-INF->lib folder.

asm-3.1.jar
jackson-core-asl-1.9.2.jar
jackson-jaxrs-1.9.2.jar
jackson-mapper-asl-1.9.2.jar
jackson-xc-1.9.2.jar
jersey-client-1.18.jar
jersey-core-1.18.jar
jersey-json-1.18.jar
jersey-server-1.18.jar
jersey-servlet-1.18.jar
jose4j-0.4.4.jar
jsr311-api-1.1.1.jar
log4j-1.2.17.jar
mongo-java-driver-3.0.2.jar
persistence-api-1.0.2.jar
slf4j-api-1.7.13.jar

Complete Project Overview

I have added the project overview to give you a full view of the structure and show you all files contained in this sample project.

jax-rs_jwt_security_proj

RESTful Web Service End Points

Restricted URIs will be shown with LOCK icon in the table below.

#URIMethodDescription
1/rest/security/statusGETDisplays the current status of the API being used. Non-restricted REST end-point
2/rest/security/authenticateGETAuthenticates the user by using the username and password that is passed in the header against the User in the User Collection in MongoDB data store.
3/rest/security/finditembyidGETSearches for an item by its ID returning the item as a JSON object. **Restricted REST end-point
4/rest/security/showallitemsGETRetrieves all items in our MongoDB datastore return the entire collection as a JSON array. **Restricted REST end-point

JSON Web Token Authorization and Authentication Communication Flow

jwt_auth_flow

1 – Initial Request for Protected Resource

curl -H 'Content-Type: application/json' -H 'username: apacheuser' 
-H 'password: Summer95!' -v -X GET  
http://localhost:8888/JwtSecurityExample/rest/security/authenticate

* About to connect() to localhost port 8888 (#0)
* Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 8888 (#0)
> GET /JwtSecurityExample/rest/security/authenticate HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 
  NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: localhost:8888
> Accept: */*
> Content-Type: application/json
> username: apacheuser
> password: Summer95!

Username/Password in Header

For security reason we recommend that the username and password be included in the HTTP Headers instead of HTTP Parameters via (@QueryParam). This is especially important when using TLS/SSL as it will guarantee that request data is encrypted end to end and prevent man in the middle attacks.

2 – Server Responds with Success Code and Payload (on Success)

< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Mon, 21 Dec 2015 15:36:47 GMT
<
* Connection #0 to host localhost left intact
* Closing connection #0
eyJraWQiOiIxIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJhdmFsZGVzLmNvbSIsImV4cCI6MTQ1M
DcxMjgwNywianRpIjoiNUFXYW5IVWNMY3E2SW9iLTB5UnNTZyIsImlhdCI6MTQ1MDcxMjIwNywib
mJmIjoxNDUwNzEyMDg3LCJzdWIiOiJhcGFjaGV1c2VyIiwicm9sZXMiOlsiY2xpZW50IiwiYWRta
W4iXX0.ZayF3TpYuz00Q_m6Fw-zlFBqvABBx9pEZdkQtkxilPAZSqTzxZYkQKYaZJN_UP2Ov46RT
uYc1baCU6J6GWfpnq13YNtV9Qon_0kJcsb9vjGhJW8_XzJZVrQCvff8PtMdhJen_J7fin_Pr9FBy
Gt5RFFHBYxBh-asxXg0XPFNn8IDsgRLN355O8n7tLjeBCXVO85Fj1hOYWDrEoHdVJe245TfPKq7T
bCuPmlKPHpguAsbWkQMGdj7mufvZ5egsegu5cLAvrbWPmQFD190hBuSTkVnFPejLQ21jlqmMuKbf
scHf0H85GPval_7sLzeQSZ7dI6FPHDLBFnBUhotqoUTMA

3 – JSON Web Token in Header

All subsequent calls should contain this JWT Token as shown below (carriage returns have been added for readability).

curl -H 'Content-Type: application/json' -H 'token: eyJraWQiOiIxIiwiYWxnIjoiUl
MyNTYifQ.eyJpc3MiOiJhdmFsZGVzLmNvbSIsImV4cCI6MTQ1MDcxNzcyNywianRpIjoiQ2w1amVlb
TFKM2NCVlFrMlhvLWoyQSIsImlhdCI6MTQ1MDcxNzEyNywibmJmIjoxNDUwNzE3MDA3LCJzdWIiOiJ
hcGFjaGV1c2VyIiwicm9sZXMiOlsiY2xpZW50IiwiYWRtaW4iXX0.bthY1dfWoXsc-xS5kMApdnbMq
vFywVX9JzRjwR2E_RoH0LvQM5iMZaNn5x5LI-9qvfT-ZmF9OrartBqW1fWIN0nUQOmX-2TAo8HDZXX
EqMvNJkAVdBGJ6CfJ1EdN9DDXVUGuwDua6cqqQAfYz5_Dc6m11Sl0MG54qsWJo_KvTm2h5FvPCdXqt
xtFkjYHJl3urZtomP3aiLHmc4l_M-zOBOp29j1TLRY930gICaHg7QklA8NbBgemnpqZ8whQVyP8ThH
IeP78VEtqnT7qizJkOjck7M0PcUxeLy1PveiPvgNV5d1rWY04gSQDjDL_PJLDhs6dmpdnApRQwpjvK
0y8IA' -v 
-X GET  http://localhost:8888/JwtSecurityExample/rest/security/showallitems

* About to connect() to localhost port 8888 (#0)
*   Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 8888 (#0)
> GET /JwtSecurityExample/rest/security/showallitems HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 
  NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: localhost:8888
> Accept: */*
> Content-Type: application/json
> token: eyJraWQiOiIxIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJhdmFsZGVzLmNvbSIsImV4c
CI6MTQ1MDcxNzcyNywianRpIjoiQ2w1amVlbTFKM2NCVlFrMlhvLWoyQSIsImlhdCI6MTQ1MDcxNz
EyNywibmJmIjoxNDUwNzE3MDA3LCJzdWIiOiJhcGFjaGV1c2VyIiwicm9sZXMiOlsiY2xpZW50Iiw
iYWRtaW4iXX0.bthY1dfWoXsc-xS5kMApdnbMqvFywVX9JzRjwR2E_RoH0LvQM5iMZaNn5x5LI-9q
vfT-ZmF9OrartBqW1fWIN0nUQOmX-2TAo8HDZXXEqMvNJkAVdBGJ6CfJ1EdN9DDXVUGuwDua6cqqQ
AfYz5_Dc6m11Sl0MG54qsWJo_KvTm2h5FvPCdXqtxtFkjYHJl3urZtomP3aiLHmc4l_M-zOBOp29j
1TLRY930gICaHg7QklA8NbBgemnpqZ8whQVyP8ThHIeP78VEtqnT7qizJkOjck7M0PcUxeLy1Pvei
PvgNV5d1rWY04gSQDjDL_PJLDhs6dmpdnApRQwpjvK0y8IA
>

4 – Server Responds with Success Code and Payload (on Success)

< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Mon, 21 Dec 2015 17:00:04 GMT
<
* Connection #0 to host localhost left intact
* Closing connection #0
[{"_id":"10029T1","item-id":"123","item-name":"KitchenAid Artisan 5 qt. 
Stand Mixer","price":314.99,"quantity":13},{"_id":"12349K1",
"item-id":"k10001", "item-name":"Keurig K10 MINI Plus Brewing System",
"price":79.99,"quantity":36}, {"_id":"83949PT","item-id":"EPP1029",
"item-name":"Electric Power Pressure Cooker XL (8 qt)",
"price":119.99,"quantity":8}]

The Item Model (Item.java)

This will be used to as the object which we store and retrieve in order to test out our application. I added it because I wanted my web service to store and retrieve some Java object.

package com.avaldes.model;

import javax.persistence.Id;

import org.codehaus.jackson.annotate.JsonProperty;

public class Item {
  @Id
  private String _id;
  private String itemId;
  private String itemName;
  private double itemPrice;
  private int itemQuantity;
  
  public Item() {}
  
  public Item(String _id, String itemId, String itemName, 
      double itemPrice, int itemQuantity) {
    
    super();
    this._id = _id;
    this.itemId = itemId;
    this.itemName = itemName;
    this.itemPrice = itemPrice;
    this.itemQuantity = itemQuantity;
  }

  public String get_id() {
    return _id;
  }
  
  public void set_id(String _id) {
    this._id = _id;
  }
  
  @JsonProperty(value = "item-id")
  public String getItemId() {
    return itemId;
  }
  
  public void setItemId(String itemId) {
    this.itemId = itemId;
  }
  
  @JsonProperty(value = "item-name")
  public String getItemName() {
    return itemName;
  }
  
  public void setItemName(String itemName) {
    this.itemName = itemName;
  }
  
  @JsonProperty(value = "price")
  public double getItemPrice() {
    return itemPrice;
  }
  
  public void setItemPrice(double itemPrice) {
    this.itemPrice = itemPrice;
  }
  
  @JsonProperty(value = "quantity")
  public int getItemQuantity() {
    return itemQuantity;
  }
  
  public void setItemQuantity(int itemQuantity) {
    this.itemQuantity = itemQuantity;
  }

  @Override
  public String toString() {
    return "Item [_id=" + _id + ", itemId=" + itemId + ", itemName="
        + itemName + ", itemPrice=" + itemPrice + ", itemQuantity="
        + itemQuantity + "]";
  }
}

The User Model (User.java)

This will be used to as the object which we store and retrieve in order to test out our application. I added it because I wanted my web service to store and retrieve some Java object.

package com.avaldes.model;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Id;

import org.codehaus.jackson.annotate.JsonProperty;

public class User {
  @Id
  private String _id;
  private String username;
  private String password;
  private String firm;
  private List<String> rolesList = new ArrayList<String>();
  
  public User() {}

  public User(String _id, String username, 
								String password, List<String> rolesList) {
    super();
    this._id = _id;
    this.username = username;
    this.password = password;
    this.rolesList = rolesList;
  }

  @JsonProperty(value = "_id")
  public String get_id() {
    return _id;
  }
  
  public void set_id(String _id) {
    this._id = _id;
  }

  @JsonProperty(value = "username")
  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  @JsonProperty(value = "password")
  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  @JsonProperty(value = "firm")
  public String getFirm() {
    return firm;
  }

  public void setFirm(String firm) {
    this.firm = firm;
  }

  @JsonProperty(value = "roles")
  public List<String> getRolesList() {
    return rolesList;
  }

  public void setRolesList(List<String> rolesList) {
    this.rolesList = rolesList;
  }

  @Override
  public String toString() {
    return "User [_id=" + _id + ", username=" + username 
			+ ", password=" + password + ", rolesList=" 
			+ rolesList + "]";
  }
}

The Singleton Class for Mongo Database (MongoDBSingleton.java)

package com.avaldes.util;

import java.io.IOException;
import java.util.Properties;

import org.apache.log4j.Logger;

import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.MongoDatabase;

public class MongoDBSingleton {
  static Logger logger = Logger.getLogger(MongoDBSingleton.class);
  private static final String properties_filename 
																= "mongodb.properties";
  
  private static MongoClient mongo            = null;
  private static MongoDatabase mongoDatabase  = null;
  private static String hostname              = null;
  private static int port                     = 0;
  private static String username              = null;
  private static String password              = null;
  private static String database              = null;
  
  private static class Holder {
    private static final MongoDBSingleton instance 
			= new MongoDBSingleton();
  }
  
  private MongoDBSingleton() {
    logger.info("Inside MongoDBSingleton...");
    ClassLoader classLoader 
			= Thread.currentThread().getContextClassLoader();
    Properties properties = new Properties();
    try {
      logger.info("Reading mongo.properties...");
      properties.load(
				classLoader.getResourceAsStream(properties_filename));
      hostname = properties.getProperty("mongodb.hostname");
      logger.info("mongodb.hostname....: " + hostname);
      String portStr = properties.getProperty("mongodb.port");
      port = Integer.parseInt(portStr);
      logger.info("mongodb.port........: " + port);
      username = properties.getProperty("mongodb.username");
      logger.info("mongodb.username....: " + username);
      password = properties.getProperty("mongodb.password");
      logger.info("mongodb.password....: " + password);
      database = properties.getProperty("mongodb.database");
      logger.info("mongodb.database....: " + database);
      
    } catch (IOException e) {
      e.printStackTrace();
    }
  };
  
  public static MongoDBSingleton getInstance() {
    return Holder.instance;
  }
  
  public MongoClient getMongoClient() {
    String URI = String.format("mongodb://%s:%s@%s:%d/?authSource=%s",
				username, password, hostname, port, database); 
    MongoClientURI mongoClientURI = new MongoClientURI(URI);
    mongo = new MongoClient(mongoClientURI);
    return mongo;
  }
  
  public MongoDatabase getDatabase() {
    if (mongoDatabase == null) {
      mongo = getMongoClient();
    }
    return mongo.getDatabase(database);
  }
}

Complete Program (JwtSecurityExample.java)

package com.avaldes.service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.apache.log4j.Logger;
import org.bson.Document;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.JsonWebKeySet;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;

import com.avaldes.model.Item;
import com.avaldes.model.StatusMessage;
import com.avaldes.model.User;
import com.avaldes.util.MongoDBSingleton;
import com.mongodb.BasicDBObject;
import com.mongodb.client.MongoDatabase;
import com.sun.jersey.api.client.ClientResponse.Status;

@Path("/security")
public class JwtSecurityExample {
  static Logger logger = Logger.getLogger(JwtSecurityExample.class);
  static List<JsonWebKey> jwkList = null;
  
  static {    
    logger.info("Inside static initializer...");
    jwkList = new LinkedList<>(); 
    for (int kid = 1; kid <= 3; kid++) { 
      JsonWebKey jwk = null;
      try {
        jwk = RsaJwkGenerator.generateJwk(2048); 
        logger.info("PUBLIC KEY (" + kid + "): " 
					+ jwk.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY));
      } catch (JoseException e) {
        e.printStackTrace();
      } 
      jwk.setKeyId(String.valueOf(kid));  
      jwkList.add(jwk); 
    } 
  }
  
  @Path("/status")
  @GET
  @Produces(MediaType.TEXT_HTML)
  public String returnVersion() {
    return "JwtSecurityExample Status is OK...";
  }

  @Path("/authenticate")
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public Response authenticateCredentials(@HeaderParam("username") 
	String username,
      @HeaderParam("password") String password)
      throws JsonGenerationException, JsonMappingException, 
							IOException {

    logger.info("Authenticating User Credentials...");

    if (username == null) {
      StatusMessage statusMessage = new StatusMessage();
      statusMessage.setStatus(
					Status.PRECONDITION_FAILED.getStatusCode());
      statusMessage.setMessage("Username value is missing!!!");
      return Response.status(
					Status.PRECONDITION_FAILED.getStatusCode())
          .entity(statusMessage).build();
    }
    
    if (password == null) {
      StatusMessage statusMessage = new StatusMessage();
      statusMessage.setStatus(
				Status.PRECONDITION_FAILED.getStatusCode());
      statusMessage.setMessage("Password value is missing!!!");
      return Response.status(
					Status.PRECONDITION_FAILED.getStatusCode())
          .entity(statusMessage).build();
    }

    User user = validUser(username, password); 
    if (user == null) {
      StatusMessage statusMessage = new StatusMessage();
      statusMessage.setStatus(Status.FORBIDDEN.getStatusCode());
      statusMessage.setMessage(
				"Access Denied for this functionality !!!");
      return Response.status(Status.FORBIDDEN.getStatusCode())
          .entity(statusMessage).build();
    }

    RsaJsonWebKey senderJwk = (RsaJsonWebKey) jwkList.get(0);
    
    senderJwk.setKeyId("1");
    logger.info("JWK (1) ===> " + senderJwk.toJson());

    // Create the Claims, which will be the content of the JWT
    JwtClaims claims = new JwtClaims();
    claims.setIssuer("avaldes.com");
    claims.setExpirationTimeMinutesInTheFuture(10);
    claims.setGeneratedJwtId();
    claims.setIssuedAtToNow();
    claims.setNotBeforeMinutesInThePast(2);
    claims.setSubject(user.getUsername());
    claims.setStringListClaim("roles", user.getRolesList()); 
 
    JsonWebSignature jws = new JsonWebSignature();

    jws.setPayload(claims.toJson());
    
    jws.setKeyIdHeaderValue(senderJwk.getKeyId());
    jws.setKey(senderJwk.getPrivateKey());
    
    jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); 

    String jwt = null;
    try {
      jwt = jws.getCompactSerialization();
    } catch (JoseException e) {
      e.printStackTrace();
    }

    return Response.status(200).entity(jwt).build();
  }
  
  // --- Protected resource using JWT Tokens` ---
  @Path("/finditembyid")
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public Response findItemById(@HeaderParam("token") String token,
      @QueryParam("itemid") String item_id) 
      throws JsonGenerationException, 
						JsonMappingException, IOException {

    Item item = null;

    logger.info("Inside findOrderById...");

    if (token == null) {
      StatusMessage statusMessage = new StatusMessage();
      statusMessage.setStatus(Status.FORBIDDEN.getStatusCode());
      statusMessage.setMessage(
				"Access Denied for this functionality !!!");
      return Response.status(Status.FORBIDDEN.getStatusCode())
          .entity(statusMessage).build();
    }

    JsonWebKeySet jwks = new JsonWebKeySet(jwkList); 
    JsonWebKey jwk = jwks.findJsonWebKey("1", null,  null,  null);
    logger.info("JWK (1) ===> " + jwk.toJson());
    
    // Validate Token's authenticity and check claims
    JwtConsumer jwtConsumer = new JwtConsumerBuilder()
      .setRequireExpirationTime() 
      .setAllowedClockSkewInSeconds(30)
      .setRequireSubject()
      .setExpectedIssuer("avaldes.com")
      .setVerificationKey(jwk.getKey())
      .build();

    try {
      //  Validate the JWT and process it to the Claims
      JwtClaims jwtClaims = jwtConsumer.processToClaims(token);
      logger.info("JWT validation succeeded! " + jwtClaims);
    } catch (InvalidJwtException e) {
      logger.error("JWT is Invalid: " + e);
      StatusMessage statusMessage = new StatusMessage();
      statusMessage.setStatus(Status.FORBIDDEN.getStatusCode());
      statusMessage.setMessage(
				"Access Denied for this functionality !!!");
      return Response.status(Status.FORBIDDEN.getStatusCode())
          .entity(statusMessage).build();
    }
    
    MongoDBSingleton mongoDB = MongoDBSingleton.getInstance();
    MongoDatabase db = mongoDB.getDatabase();

    BasicDBObject query = new BasicDBObject();
    query.put("_id", item_id);
    List<Document> results = db.getCollection("items").find(query)
        .into(new ArrayList<Document>());
    int size = results.size();

    if (size == 0) {
      StatusMessage statusMessage = new StatusMessage();
      statusMessage.setStatus(
				Status.PRECONDITION_FAILED.getStatusCode());
      statusMessage.setMessage("Unable to find that item !!!");
      return Response.status(
					Status.PRECONDITION_FAILED.getStatusCode())
          .entity(statusMessage).build();
    }

    for (Document current : results) {
      ObjectMapper mapper = new ObjectMapper();
      try {
        logger.info(current.toJson());
        item = mapper.readValue(current.toJson(), Item.class);
      } catch (JsonParseException e) {
        e.printStackTrace();
      } catch (JsonMappingException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    
    return Response.status(200).entity(item).build();
  }

  // --- Protected resource using JWT Token ---
  @Path("/showallitems")
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public Response showAllItems(@HeaderParam("token") String token) 
      throws JsonGenerationException,
    JsonMappingException, IOException {

    Item item = null;

    logger.info("Inside showAllItems...");

    if (token == null) {
      StatusMessage statusMessage = new StatusMessage();
      statusMessage.setStatus(Status.FORBIDDEN.getStatusCode());
      statusMessage.setMessage(
				"Access Denied for this functionality !!!");
      return Response.status(Status.FORBIDDEN.getStatusCode())
          .entity(statusMessage).build();
    }

    JsonWebKeySet jwks = new JsonWebKeySet(jwkList); 
    JsonWebKey jwk = jwks.findJsonWebKey("1", null,  null,  null);
    logger.info("JWK (1) ===> " + jwk.toJson());
    
    // Validate Token's authenticity and check claims
    JwtConsumer jwtConsumer = new JwtConsumerBuilder()
      .setRequireExpirationTime()
      .setAllowedClockSkewInSeconds(30)
      .setRequireSubject()
      .setExpectedIssuer("avaldes.com")
      .setVerificationKey(jwk.getKey())
      .build();

    try {
      //  Validate the JWT and process it to the Claims
      JwtClaims jwtClaims = jwtConsumer.processToClaims(token);
      logger.info("JWT validation succeeded! " + jwtClaims);
    } catch (InvalidJwtException e) {
      logger.error("JWT is Invalid: " + e);
      StatusMessage statusMessage = new StatusMessage();
      statusMessage.setStatus(Status.FORBIDDEN.getStatusCode());
      statusMessage.setMessage(
				"Access Denied for this functionality !!!");
      return Response.status(
					Status.FORBIDDEN.getStatusCode())
          .entity(statusMessage).build();
    }
    
    MongoDBSingleton mongoDB = MongoDBSingleton.getInstance();
    MongoDatabase db = mongoDB.getDatabase();

    List<Document> results = db.getCollection("items").find()
        .into(new ArrayList<Document>());
    int size = results.size();

    if (size == 0) {
      StatusMessage statusMessage = new StatusMessage();
      statusMessage.setStatus(
				Status.PRECONDITION_FAILED.getStatusCode());
      statusMessage.setMessage("There are no Items to display !!!");
      return Response.status(
					Status.PRECONDITION_FAILED.getStatusCode())
          .entity(statusMessage).build();
    }

    List<Item> allItems = new ArrayList<Item>();
    for (Document current : results) {
      ObjectMapper mapper = new ObjectMapper();
      try {
        logger.info(current.toJson());
        item = mapper.readValue(current.toJson(), Item.class);
        allItems.add(item);
      } catch (JsonParseException e) {
        e.printStackTrace();
      } catch (JsonMappingException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    return Response.status(200).entity(allItems).build();
  }
  
  private User validUser(String username, String password) {
    MongoDBSingleton mongoDB = MongoDBSingleton.getInstance();
    MongoDatabase db = mongoDB.getDatabase();
    List<Document> results = null;

    results = db.getCollection("users")
				.find(new Document("username", username))
        .limit(1).into(new ArrayList<Document>());
    int size = results.size();

    if (size == 1) {
      for (Document current : results) {
        ObjectMapper mapper = new ObjectMapper();
        User user = null;
        try {
          // logger.info(current.toJson());
          user = mapper.readValue(current.toJson(), User.class);
        } catch (JsonParseException e) {
          e.printStackTrace();
        } catch (JsonMappingException e) {
          e.printStackTrace();
        } catch (IOException e) {
          e.printStackTrace();
        }
        if (user != null && username.equals(user.getUsername())
            && password.equals(user.getPassword())) {
          return user;
        } else {
          return null;
        }
      }
      return null;
    } else {
      return null;
    }
  }
}

LOG4J Configuration File (log4j.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "
    -//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">

	<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

  <!-- Appenders -->
  <appender name="console" class="org.apache.log4j.ConsoleAppender">
  <param name="Target" value="System.out" />
    <layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" value="%-5p: %c - %m%n" />
    </layout>
  </appender>
  
  <!-- Application Loggers -->
  <logger name="com.avaldes">
    <level value="info" />
  </logger>

  <!-- Root Logger -->
  <root>
    <priority value="warn" />
    <appender-ref ref="console" />
  </root>
</log4j:configuration>

Web Deployment Descriptor (web.xml)

This is a pretty straight forward deployment descriptor file – only thing you need to add is the location of you java package in the Jersey ServletContainer entry as init-param. Please ensure you add it to the web.xml file as shown below.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation=
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
id="WebApp_ID" version="3.0">

  <display-name>JAX-RS JSON Web Token Application</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>
			com.sun.jersey.spi.container.servlet.ServletContainer
		</servlet-class>
    <init-param>
      <param-name>
				com.sun.jersey.config.property.packages
			</param-name>
      <param-value>com.avaldes</param-value>
    </init-param>
    <init-param>
      <param-name>
				com.sun.jersey.api.json.POJOMappingFeature
			</param-name>
      <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

MongoDB Items Collections

{
    "_id" : "10029T1",
    "item-id" : "123",
    "item-name" : "KitchenAid Artisan 5 qt. Stand Mixer",
    "price" : 314.9900000000000100,
    "quantity" : 13
}
{
    "_id" : "12349K1",
    "item-id" : "k10001",
    "item-name" : "Keurig K10 MINI Plus Brewing System",
    "price" : 79.9899999999999950,
    "quantity" : 36
}
{
    "_id" : "83949PT",
    "item-id" : "EPP1029",
    "item-name" : "Electric Power Pressure Cooker XL (8 qt)",
    "price" : 119.9899999999999900,
    "quantity" : 8
}

MongoDB Users Collections

{
    "_id" : "1",
    "username" : "apacheuser",
    "password" : "Summer95!",
    "firm"     : "Apache",
    "roles"		 : ["client", "admin"]
}
{
    "_id" : "2",
    "username" : "springuser",
    "password" : "Spring99!",
    "firm"     : "SpringSource",
    "roles"		 : ["client"]
}
{
    "_id" : "3",
    "username" : "user3",
    "password" : "Autumn03!",
    "firm"     : "RedHat",
    "roles"		 : ["client"]
}

MongoDB Property File

We will be storing all of the MongoDB Database credentials in a property file that will only be accessible by the application running on the server.

#----MongoDB Database Details-----
mongodb.hostname=localhost
mongodb.port=27017
mongodb.username=webuser
mongodb.password=W3b$ervic3s!
mongodb.database=jwtDB

Testing out the Web Services

To test out the application I used both CURL and Postman which is a Google Chrome Application. Using this tool I validated each of the REST API calls. Please review the screen shots below:

Using CURL to Test out the JWT Authorization and Authentication

curl -H 'Content-Type: application/json' -H 'username: apacheuser' -H 'password: Summer95!' -v -X GET  http://localhost:8888/JwtSecurityExample/rest/security/authenticate
* About to connect() to localhost port 8888 (#0)
* Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 8888 (#0)
> GET /JwtSecurityExample/rest/security/authenticate HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: localhost:8888
> Accept: */*
> Content-Type: application/json
> username: apacheuser
> password: Summer95!

< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Mon, 21 Dec 2015 15:36:47 GMT
<
* Connection #0 to host localhost left intact
* Closing connection #0
eyJraWQiOiIxIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJhdmFsZGVzLmNvbSIsImV4cCI6MTQ1MDcxMjgwNywianRpIjoiNUFXYW5IVWNMY3E2SW9iLTB5UnNTZyIsImlhdCI6MTQ1MDcxMjIwNywibmJmIjoxNDUwNzEyMDg3LCJzdWIiOiJhcGFjaGV1c2VyIiwicm9sZXMiOlsiY2xpZW50IiwiYWRtaW4iXX0.ZayF3TpYuz00Q_m6Fw-zlFBqvABBx9pEZdkQtkxilPAZSqTzxZYkQKYaZJN_UP2Ov46RTuYc1baCU6J6GWfpnq13YNtV9Qon_0kJcsb9vjGhJW8_XzJZVrQCvff8PtMdhJen_J7fin_Pr9FByGt5RFFHBYxBh-asxXg0XPFNn8IDsgRLN355O8n7tLjeBCXVO85Fj1hOYWDrEoHdVJe245TfPKq7TbCuPmlKPHpguAsbWkQMGdj7mufvZ5egsegu5cLAvrbWPmQFD190hBuSTkVnFPejLQ21jlqmMuKbfscHf0H85GPval_7sLzeQSZ7dI6FPHDLBFnBUhotqoUTMA

------------------------------------------------------------

curl -H 'Content-Type: application/json' -H 'token: eyJraWQiOiIxIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJhdmFsZGVzLmNvbSIsImV4cCI6MTQ1MDcxNzcyNywianRpIjoiQ2w1amVlbTFKM2NCVlFrMlhvLWoyQSIsImlhdCI6MTQ1MDcxNzEyNywibmJmIjoxNDUwNzE3MDA3LCJzdWIiOiJhcGFjaGV1c2VyIiwicm9sZXMiOlsiY2xpZW50IiwiYWRtaW4iXX0.bthY1dfWoXsc-xS5kMApdnbMqvFywVX9JzRjwR2E_RoH0LvQM5iMZaNn5x5LI-9qvfT-ZmF9OrartBqW1fWIN0nUQOmX-2TAo8HDZXXEqMvNJkAVdBGJ6CfJ1EdN9DDXVUGuwDua6cqqQAfYz5_Dc6m11Sl0MG54qsWJo_KvTm2h5FvPCdXqtxtFkjYHJl3urZtomP3aiLHmc4l_M-zOBOp29j1TLRY930gICaHg7QklA8NbBgemnpqZ8whQVyP8ThHIeP78VEtqnT7qizJkOjck7M0PcUxeLy1PveiPvgNV5d1rWY04gSQDjDL_PJLDhs6dmpdnApRQwpjvK0y8IA' -v -X GET  http://localhost:8888/JwtSecurityExample/rest/security/showallitems

* About to connect() to localhost port 8888 (#0)
*   Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 8888 (#0)
> GET /JwtSecurityExample/rest/security/showallitems HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 
  NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: localhost:8888
> Accept: */*
> Content-Type: application/json
> token: eyJraWQiOiIxIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJhdmFsZGVzLmNvbSIsImV4cCI6MTQ1MDcxNzcyNywianRpIjoiQ2w1amVlbTFKM2NCVlFrMlhvLWoyQSIsImlhdCI6MTQ1MDcxNzEyNywibmJmIjoxNDUwNzE3MDA3LCJzdWIiOiJhcGFjaGV1c2VyIiwicm9sZXMiOlsiY2xpZW50IiwiYWRtaW4iXX0.bthY1dfWoXsc-xS5kMApdnbMqvFywVX9JzRjwR2E_RoH0LvQM5iMZaNn5x5LI-9qvfT-ZmF9OrartBqW1fWIN0nUQOmX-2TAo8HDZXXEqMvNJkAVdBGJ6CfJ1EdN9DDXVUGuwDua6cqqQAfYz5_Dc6m11Sl0MG54qsWJo_KvTm2h5FvPCdXqtxtFkjYHJl3urZtomP3aiLHmc4l_M-zOBOp29j1TLRY930gICaHg7QklA8NbBgemnpqZ8whQVyP8ThHIeP78VEtqnT7qizJkOjck7M0PcUxeLy1PveiPvgNV5d1rWY04gSQDjDL_PJLDhs6dmpdnApRQwpjvK0y8IA

< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Mon, 21 Dec 2015 17:00:04 GMT
<
* Connection #0 to host localhost left intact
* Closing connection #0
[{"_id":"10029T1","item-id":"123","item-name":"KitchenAid Artisan 5 qt. Stand Mixer","price":314.99,"quantity":13},{"_id":"12349K1","item-id":"k10001","item-name":"Keurig K10 MINI Plus Brewing System","price":79.99,"quantity":36},{"_id":"83949PT","item-id":"EPP1029","item-name":"Electric Power Pressure Cooker XL (8 qt)","price":119.99,"quantity":8}]
------------------------------------------------------------

curl -H 'Content-Type: application/json' -H 'token: eyJraWQiOiIxIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJhdmFsZGVzLmNvbSIsImV4cCI6MTQ1MDcxODYxMywianRpIjoiMlR0YkU3MThvY2tmTTBwTWRHX0VNdyIsImlhdCI6MTQ1MDcxODAxMywibmJmIjoxNDUwNzE3ODkzLCJzdWIiOiJhcGFjaGV1c2VyIiwicm9sZXMiOlsiY2xpZW50IiwiYWRtaW4iXX0.Ox1sw51OmnNFgN6gTBNpx73vZFp1WJOy7gvCT-AxYrsh2P7W_QVsfOtB7eNkMlgdXdcGbpuvZPygRPlXgtmx1HiQjkCi-jphsoEUp6KM5-Q7YD43OtypQRzOnppuHqxANz1jb0jd4OrEY9hOtbJjT3Ltu6GmGCBBJ1r9udaT2RUr2B2fFOs4edR6-eZp1olLd8-IuzkVgdCAwOFP6wDCPaQa1S13i7OnyDDDiH8IOmQFBNgdBBh3Px8TgWvJ247zKlL-yJqU0NpLg4UiqSGHn_Ru-5OKgCvJW6lRo6Gv-JW7uoKbEfIdR7F6t0nbLO5cAkTzklUNKbLw7UI7V-Hztg' -v -X GET  http://localhost:8888/JwtSecurityExample/rest/security/finditembyid?itemid=12349K1
* About to connect() to localhost port 8888 (#0)
*   Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 8888 (#0)
> GET /JwtSecurityExample/rest/security/finditembyid?itemid=12349K1 HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: localhost:8888
> Accept: */*
> Content-Type: application/json
> token: eyJraWQiOiIxIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJhdmFsZGVzLmNvbSIsImV4cCI6MTQ1MDcxODYxMywianRpIjoiMlR0YkU3MThvY2tmTTBwTWRHX0VNdyIsImlhdCI6MTQ1MDcxODAxMywibmJmIjoxNDUwNzE3ODkzLCJzdWIiOiJhcGFjaGV1c2VyIiwicm9sZXMiOlsiY2xpZW50IiwiYWRtaW4iXX0.Ox1sw51OmnNFgN6gTBNpx73vZFp1WJOy7gvCT-AxYrsh2P7W_QVsfOtB7eNkMlgdXdcGbpuvZPygRPlXgtmx1HiQjkCi-jphsoEUp6KM5-Q7YD43OtypQRzOnppuHqxANz1jb0jd4OrEY9hOtbJjT3Ltu6GmGCBBJ1r9udaT2RUr2B2fFOs4edR6-eZp1olLd8-IuzkVgdCAwOFP6wDCPaQa1S13i7OnyDDDiH8IOmQFBNgdBBh3Px8TgWvJ247zKlL-yJqU0NpLg4UiqSGHn_Ru-5OKgCvJW6lRo6Gv-JW7uoKbEfIdR7F6t0nbLO5cAkTzklUNKbLw7UI7V-Hztg
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Mon, 21 Dec 2015 17:17:28 GMT
<
* Connection #0 to host localhost left intact
* Closing connection #0
{"_id":"12349K1","item-id":"k10001","item-name":"Keurig K10 MINI Plus Brewing System","price":79.99,"quantity":36}

Testing out using POSTMAN Chrome Extension

Download the Complete Source Code

That’s It!

I hope you enjoyed this tutorial. It was certainly a lot of fun putting it together and testing it out. Please continue to share the love and like us so that we can continue bringing you quality tutorials. Happy Coding!!!

java_jaxrs_jwt_security

Related JAX-RS Tutorial Posts

  • RESTful Web Services with AngularJS, Bootstrap and Java using JAX-RS and Jersey
    In this tutorial we will develop a full blown CRUD application using JAX-RS API and Jersey to implement RESTful web services. JAX-RS stands for Java API for RESTful Web Services and by using this powerful API developers can easily build REST services. Jersey RESTful Web Services is an open source framework for developing RESTful Web Services in Java that provides support for JAX-RS APIs.
  • JAX-RS Security using JSON Web Encryption(JWE) with AngularJS, Bootstrap, Grid-UI and MongoDB Example
    In this tutorial we will discuss how to use AngularJS, Bootstrap and Grid-UI to connect to secure JAX-RS RESTful web services using JWE/JWT/JWS for Authentication and Authorization. In our example implementation, we will be using Symmetric Encryption where the receiver and sender share a common key.
  • JAX-RS Security using JSON Web Encryption (JWE) with JWS/JWT for Authentication and Authorization
    In this tutorial we will discuss how to secure JAX-RS RESTful web services using JSON Web Encryption(JWE), JSON Web Key (JWK), JSON Web Signature(JWS), and JSON Web Tokens(JWT) for Authentication and Authorization. JSON Web Encryption (JWE) encrypted content using Javascript Object Notation (JSON) based structures.
  • JAX-RS Security using JSON Web Tokens (JWT) for Authentication and Authorization
    In this tutorial we will discuss how to secure JAX-RS RESTful web services using JSON Web Tokens Authentication and Authorization. This form of security is used for authenticating a client using a signed token which can be verified by application servers. This token-based form of security is a ideal candidate for Cross-domain (CORS) access and when server-side scalability is a prime motivation factor.
  • JAX-RS Security using API-KEY for Authorization
    In this tutorial we will discuss how to secure JAX-RS RESTful web services using API-KEY or Service Key for Authorization. This form of security is used to ensure that certain RESTful endpoints are protected against unauthorized use.
  • JAX-RS Security using Digest Authentication and Authorization
    In this JAX-RS Digest Authentication and Authorization tutorial we will discuss how to set up digest security for our RESTful web service. This form of access authentication is slightly more complex than the previously discussed JAX-RS Basic Authentication Tutorial.
  • JAX-RS Security using Basic Authentication and Authorization
    In this JAX-RS basic authentication and authorization tutorial we will discuss how to set up security for our RESTful web service. We will need to ensure that some of the URIs are protected and only clients that have been authenticated and authorized are able to gain access and make use of them.
  • Upload and Download Multiple Binary Files using MongoDB
    In this tutorial we are going to develop multiple file upload and file download capability using RESTful web service using JAX-RS and Jersey storing the contents of files into MongoDB Database using a powerful feature in MongoDB for managing large files called GridFS.
  • Inserting and Retrieving Binary Data with MongoDB using JAX-RS RESTful Web Service
    In this tutorial we are going to develop file upload and file download capability using RESTful web service using JAX-RS and Jersey storing the contents of files into MongoDB Database using a powerful feature in MongoDB for managing large files called GridFS. The bulk of the framework for this tutorial came from my previous tutorial so you find many similarities between the two posts.
  • Inserting and Retrieving Binary Data with SQL Server Database using JAX-RS RESTful Web Service
    In this tutorial we are going to develop file upload and file download capability using RESTful web service using JAX-RS and Jersey storing the contents of the file into SQL Server Database using BLOB. Our example will be able to store PDF files, Excel files, Word Document files, Powerpoint files, Image files, or any other type of file available us provided we have ample amount of storage space.
  • File Download Example Using RESTful Web Service with JAX-RS and Jersey
    In this example we are going to develop file download capability using RESTful web service using JAX-RS and Jersey. As you will see, downloading a File using Jersey is very easy as it uses the HTTP GET for the file operations. In our web service, we will be allowing you to download the file via two mechanisms. You will be able to download by HTTP query parameters via @QueryParam and also by using the path parameters via @PathParam.
  • JAX-RS Restful Web Services with JNDI Datasource for MySQL in Tomcat
    In this tutorial we will discuss how to set up JAX-RS RESTful web services and configure a JNDI Datasource with Connection Pooling in Tomcat and connect to MYSQL Database. JNDI (Java Naming and Directory Interface) provides and interface to multiple naming and directory services.
  • File Upload Example Using RESTful Web Service with JAX-RS and Jersey
    In this example we are going to develop file upload capability using RESTful web service using JAX-RS and Jersey. As you will see, uploading a File using Jersey is pretty straight forward as it uses HTTP POST with the encoding type of multipart/form-data for the file operations.
  • RESTful Web Services @FormParam Example using JAX-RS and Jersey
    In this example we are going to develop a simple RESTful web service using JAX-RS and Jersey to extract form parameters submitted by a form using @FormParam annotation.
  • RESTful Web Services @MatrixParam Example using JAX-RS and Jersey
    In this example we are going to develop a simple RESTful web service using JAX-RS and Jersey to extract matrix parameters from the request URL using the @MatrixParam annotations.
  • RESTful Web Services @QueryParam Example using JAX-RS and Jersey
    In this example we are going to develop a simple RESTful web service using JAX-RS and Jersey to extract query parameters from the request URL using the @QueryParam annotation.
  • RESTful Web Services @PathParam Example using JAX-RS and Jersey
    In this example we are going to develop a simple RESTful web service using JAX-RS and Jersey to extract path parameters from the request URL using the @PathParam annotation.

Please Share Us on Social Media

Facebooktwittergoogle_plusredditpinterestlinkedinmail

Leave a Reply

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