JSON web token (JWT) implementation in Java

In my previous article , I talked about JWT introduction and how it works. There are multiple libraries by which you can implement JWT in Java.

1. Java JWT: JSON Web Token for Java and Android

Installation

Use your favorite Maven-compatible build tool to pull the dependency (and its transitive dependencies) from Maven Central:

Maven:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.6.0</version>
</dependency>



Gradle:


dependencies {
    compile 'io.jsonwebtoken:jjwt:0.6.0'
}

Note: JJWT depends on Jackson 2.x. If you’re already using an older version of Jackson in your app, read this

Usage

Most complexity is hidden behind a convenient and readable builder-based fluent interface, great for relying on IDE auto-completion to write code quickly. Here’s an example:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.crypto.MacProvider;
import java.security.Key;

// We need a signing key, so we'll create one just for this example. Usually
// the key would be read from your application configuration instead.
Key key = MacProvider.generateKey();

String s = Jwts.builder().setSubject("Joe").signWith(SignatureAlgorithm.HS512, key).compact();
How easy was that!?

Now let's verify the JWT (you should always discard JWTs that don't match an expected signature):

assert Jwts.parser().setSigningKey(key).parseClaimsJws(s).getBody().getSubject().equals("Joe");
You have to love one-line code snippets!

But what if signature validation failed? You can catch SignatureException and react accordingly:

try {

    Jwts.parser().setSigningKey(key).parseClaimsJws(compactJwt);

    //OK, we can trust this JWT

} catch (SignatureException e) {

    //don't trust the JWT!
}

2. Using Nimbus Jose + JWT

The most popular and robust Java library for JSON Web Tokens (JWT)
Supports all standard signature (JWS) and encryption (JWE) algorithms
Open source Apache 2.0 licence

Features –

– Signed / encrypted tokens, such as bearer access tokens in OAuth 2.0 or OpenID Connect identity tokens;
– Self-contained API keys, with optional revocation;
– Protecting content and messages;
– Authenticating clients and web API requests.

Use in Java

// Create an HMAC-protected JWS object with some payload
JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.HS256),
                                    new Payload("Hello world!"));

// We need a 256-bit key for HS256 which must be pre-shared
byte[] sharedKey = new byte[32];
new SecureRandom().nextBytes(sharedKey);

// Apply the HMAC to the JWS object
jwsObject.sign(new MACSigner(sharedKey));

// Output to URL-safe format
jwsObject.serialize(); 

Maven configuration

Maven
For Java 7+ :

<dependency>
    <groupId>com.nimbusds</groupId>
    <artifactId>nimbus-jose-jwt</artifactId>
    <version>4.11.2</version>
</dependency>

3. JSON token library – It depend on Google Guava.The library is in fact used by Google Wallet.

Here is how to create a jwt, and to verify it and deserialize it:

Maven –

<dependency>
    <groupId>com.googlecode.jsontoken</groupId>
    <artifactId>jsontoken</artifactId>
    <version>1.0</version>
</dependency>
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>18.0</version>
</dependency>

and Java implementation –

import java.security.InvalidKeyException;
import java.security.SignatureException;
import java.util.Calendar;
import java.util.List;

import net.oauth.jsontoken.JsonToken;
import net.oauth.jsontoken.JsonTokenParser;
import net.oauth.jsontoken.crypto.HmacSHA256Signer;
import net.oauth.jsontoken.crypto.HmacSHA256Verifier;
import net.oauth.jsontoken.crypto.SignatureAlgorithm;
import net.oauth.jsontoken.crypto.Verifier;
import net.oauth.jsontoken.discovery.VerifierProvider;
import net.oauth.jsontoken.discovery.VerifierProviders;

import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.joda.time.DateTime;

import com.google.common.collect.Lists;
import com.google.gson.JsonObject;


/**
 * Provides static methods for creating and verifying access tokens and such. 
 * @author davidm
 *
 */
public class AuthHelper {

    private static final String AUDIENCE = "NotReallyImportant";

    private static final String ISSUER = "YourCompanyOrAppNameHere";

    private static final String SIGNING_KEY = "[email protected]^($%*$%";

    /**
     * Creates a json web token which is a digitally signed token that contains a payload (e.g. userId to identify 
     * the user). The signing key is secret. That ensures that the token is authentic and has not been modified.
     * Using a jwt eliminates the need to store authentication session information in a database.
     * @param userId
     * @param durationDays
     * @return
     */
    public static String createJsonWebToken(String userId, Long durationDays)    {
        //Current time and signing algorithm
        Calendar cal = Calendar.getInstance();
        HmacSHA256Signer signer;
        try {
            signer = new HmacSHA256Signer(ISSUER, null, SIGNING_KEY.getBytes());
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        }

        //Configure JSON token
        JsonToken token = new net.oauth.jsontoken.JsonToken(signer);
        token.setAudience(AUDIENCE);
        token.setIssuedAt(new org.joda.time.Instant(cal.getTimeInMillis()));
        token.setExpiration(new org.joda.time.Instant(cal.getTimeInMillis() + 1000L * 60L * 60L * 24L * durationDays));

        //Configure request object, which provides information of the item
        JsonObject request = new JsonObject();
        request.addProperty("userId", userId);

        JsonObject payload = token.getPayloadAsJsonObject();
        payload.add("info", request);

        try {
            return token.serializeAndSign();
        } catch (SignatureException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Verifies a json web token's validity and extracts the user id and other information from it. 
     * @param token
     * @return
     * @throws SignatureException
     * @throws InvalidKeyException
     */
    public static TokenInfo verifyToken(String token)  
    {
        try {
            final Verifier hmacVerifier = new HmacSHA256Verifier(SIGNING_KEY.getBytes());

            VerifierProvider hmacLocator = new VerifierProvider() {

                @Override
                public List<Verifier> findVerifier(String id, String key){
                    return Lists.newArrayList(hmacVerifier);
                }
            };
            VerifierProviders locators = new VerifierProviders();
            locators.setVerifierProvider(SignatureAlgorithm.HS256, hmacLocator);
            net.oauth.jsontoken.Checker checker = new net.oauth.jsontoken.Checker(){

                @Override
                public void check(JsonObject payload) throws SignatureException {
                    // don't throw - allow anything
                }

            };
            //Ignore Audience does not mean that the Signature is ignored
            JsonTokenParser parser = new JsonTokenParser(locators,
                    checker);
            JsonToken jt;
            try {
                jt = parser.verifyAndDeserialize(token);
            } catch (SignatureException e) {
                throw new RuntimeException(e);
            }
            JsonObject payload = jt.getPayloadAsJsonObject();
            TokenInfo t = new TokenInfo();
            String issuer = payload.getAsJsonPrimitive("iss").getAsString();
            String userIdString =  payload.getAsJsonObject("info").getAsJsonPrimitive("userId").getAsString();
            if (issuer.equals(ISSUER) && !StringUtils.isBlank(userIdString))
            {
                t.setUserId(new ObjectId(userIdString));
                t.setIssued(new DateTime(payload.getAsJsonPrimitive("iat").getAsLong()));
                t.setExpires(new DateTime(payload.getAsJsonPrimitive("exp").getAsLong()));
                return t;
            }
            else
            {
                return null;
            }
        } catch (InvalidKeyException e1) {
            throw new RuntimeException(e1);
        }
    }


}

public class TokenInfo {
    private ObjectId userId;
    private DateTime issued;
    private DateTime expires;
    public ObjectId getUserId() {
        return userId;
    }
    public void setUserId(ObjectId userId) {
        this.userId = userId;
    }
    public DateTime getIssued() {
        return issued;
    }
    public void setIssued(DateTime issued) {
        this.issued = issued;
    }
    public DateTime getExpires() {
        return expires;
    }
    public void setExpires(DateTime expires) {
        this.expires = expires;
    }
}

Happy API secure using JWT with Vinay

Reference
– https://github.com/jwtk/jjwt
– http://connect2id.com/products/nimbus-jose-jwt
– https://code.google.com/archive/p/jsontoken/

Custom Role Mapping Provider in Weblogic

The default (that is, active) security realm for WebLogic Server includes a WebLogic Role Mapping provider. The WebLogic Role Mapping provider computes dynamic security roles for a specific user (subject) with respect to a specific protected WebLogic resource for each of the default users and WebLogic resources. The WebLogic Role Mapping provider supports the deployment and undeployment of security roles within the system. The WebLogic Role Mapping provider uses the same security policy engine as the WebLogic Authorization provider. If you want to use a role mapping mechanism that already exists within your organization, you could create a custom role mapping provider to tie into that system.

You need 3 Files, a XML File with the configuration, the Provider and the Implementation of a Role.

The Config File:

<?xml version="1.0" ?>
<!DOCTYPE MBeanType SYSTEM "commo.dtd">

<MBeanType
 Name          = "MYRoleMapper"
 DisplayName   = "MYRoleMapper"
 Package       = "MY.security"
 Extends       = "weblogic.management.security. authorization.RoleMapper"
 PersistPolicy = "OnUpdate"
>
 <MBeanAttribute
  Name          = "ProviderClassName"
  Type          = "java.lang.String"
  Writeable     = "false"
  Preprocessor  = "weblogic.management.configuration.LegalHelper.checkClassName(value)"
  Default       = "&quot;MY.security.MYRoleMapperProviderImpl&quot;"
 />

 <MBeanAttribute
  Name          = "Description"
  Type          = "java.lang.String"
  Writeable     = "false"
  Default       = "&quot;MY RM provider &quot;"
 />

 <MBeanAttribute
  Name          = "Version"
  Type          = "java.lang.String"
  Writeable     = "false"
  Default       = "&quot;1.2&quot;"
 />

</MBeanType>

The Actual Provider MYRoleMapperProviderImpl.java:

public class MYRoleMapperProviderImpl implements RoleProvider, RoleMapper {
    private String description;
    private static final Map<String, SecurityRole> NO_ROLES = Collections.unmodifiableMap(new HashMap<String, SecurityRole>(1));

    private final static String RESSOURCE_URL = "<url>";
    private final static String RESSOURCE_EJB = "<ejb>";

    private enum rollen {
        READER;
    }

    @Override
    public void initialize(ProviderMBean mbean, SecurityServices services) {
        description = mbean.getDescription() + "\n" + mbean.getVersion();
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public void shutdown() {

    }

    @Override
    public RoleMapper getRoleMapper() {
        return this;
    }

    @Override
    public Map<String, SecurityRole> getRoles(Subject subject, Resource resource, ContextHandler handler) {
        Map<String, SecurityRole> roles = new HashMap<String, SecurityRole>();
        Set<Principal> principals = subject.getPrincipals();
        for (Resource res = resource; res != null; res = res.getParentResource()) {
            getRoles(res, principals, roles);
        }
        if (roles.isEmpty()) {
            return NO_ROLES;
        }
        return roles;
    }

    private void getRoles(Resource resource, Set<Principal> principals, Map<String, SecurityRole> roles) {
        if (resource.getType() == RESSOURCE_URL || resource.getType() == RESSOURCE_EJB) {
                            roles.put(rollen.READER.toString(), new MYSecurityRoleImpl(rollen.READER.toString(), "READER Rolle"));          
            }
    }
}

simple Role Implementation:

package MY.security;

import weblogic.security.service.SecurityRole;

public class MYSecurityRoleImpl implements SecurityRole {

    private String _roleName;
       private String _description;
       private int _hashCode;

       public MYSecurityRoleImpl(String roleName, String description)
       {
          _roleName = roleName;
          _description = description;
          _hashCode = roleName.hashCode() + 17;
       }

       public boolean equals(Object secRole)
       {
          if (secRole == null) 
          {
             return false;
          }

          if (this == secRole) 
          {
             return true;
          }

          if (!(secRole instanceof MYSecurityRoleImpl)) 
          {
             return false;
          }

          MYSecurityRoleImpl anotherSecRole = (MYSecurityRoleImpl)secRole;

          if (!_roleName.equals(anotherSecRole.getName())) 
          {
             return false;
          }

          return true;
       }

       public String toString () { return _roleName; }
       public int hashCode () { return _hashCode; }
       public String getName () { return _roleName; }
       public String getDescription () { return _description; }
}

For more information go through documentation

Now you need to configure in weblogic admin console in security realms – providers- new

Happy learning with Vinay in techartifact…..

Create user in OID

Using the Oracle-supplied Java SDK’s
The sample code below uses the OID SDK’s to bind to the OID directory server and then create a new user under a specific location in the directory tree.

import oracle.ldap.util.*;
import oracle.ldap.util.jndi.*;
import javax.naming.NamingException;
import javax.naming.directory.*;
import java.io.*;
import java.util.*;
 
public class NewUser
{
final static String ldapServerName = "mlc2.acme.org";
final static String ldapServerPort = "3060";
final static String rootdn = "cn=orcladmin";
final static String rootpass = "welcome1";
 
public static void main(String argv[]) throws NamingException
{
// Create the connection to the ldap server
InitialDirContext ctx = ConnectionUtil.getDefaultDirCtx(ldapServerName,
ldapServerPort,
rootdn,
rootpass);
 
// Create the subscriber object using the default subscriber
Subscriber mysub = null;
String [] mystr = null;
try {
RootOracleContext roc = new RootOracleContext(ctx);
mysub = roc.getSubscriber(ctx, Util.IDTYPE_DN, "dc=acme,dc=org", mystr);
}
catch (UtilException e) {
e.printStackTrace();
}
 
// Create ModPropertySet with user information
ModPropertySet mps = new ModPropertySet();
mps.addProperty(LDIF.ATTRIBUTE_CHANGE_TYPE_ADD,"givenname", "John");
mps.addProperty(LDIF.ATTRIBUTE_CHANGE_TYPE_ADD,"mail", "[email protected]");
mps.addProperty(LDIF.ATTRIBUTE_CHANGE_TYPE_ADD,"userpassword", "welcome1");
 
// Create the user
User newUser = null;
try {
newUser = mysub.createUser(ctx, mps, true);
System.out.println("New User DN: " + newUser.getDN(ctx));
}
catch (UtilException e) {
e.printStackTrace();
}
}
}

To use the above sample code do the following:
1. Save the above text indicated between the begin/end cut lines into a file named NewUser.java. Note that the filename and case are important and must be exactly NewUser.java unless the code has been modified.

2. Locate the Java Development Kit (JDK) on your system. For recent Oracle installations it should have been installed under $ORACLE_HOME/jdk but could be located elsewhere.

3. Compile the NewUser.java file into a binary NewUser.class file using the following command:

/bin/javac –classpath $ORACLE_HOME/jlib/ldapjclnt9.jar NewUser.java

Since the sample code makes use of the Oracle LDAP classes it is necessary to include the classpath parameter in the javac command. The above command should complete with no errors or output and should result in a file named NewUser.class being created.

4. Execute the resulting NewUser.class file using the following command:

/bin/java –cp .:$ORACLE_HOME/jlib/ldapjclnt9.jar NewUser

Note that this time there is NO extension specified (neither .java nor .class is used) and it must be executed from the same directory where the NewUser.class file is located. Again the file is case-sensitive and must be exactly as listed above. Make note of the –cp parameter as it includes the current directory and the location of the ldapjclnt9.jar file. It is necessary to include the “.:” (this is a period followed by a colon) in the –cp parameter for the program to execute correctly.

If the NewUser.class file executes it will print “New User DN: cn=john.doe,cn=Users,dc=acme,dc=org” to the screen. The user will be created within OID in the cn=users container of the default subscriber/realm. This user can then been seen via an ldapsearch or through Oracle Directory Manager (ODM).

If any errors are encountered (such as invalid user/pass, duplicate entry, etc… then an error message will be displayed. In this simple example the error messages are of very little help other than for indicating what part of the code failed. For example, if the user already existed then the following error will be displayed:

oracle.ldap.util.UtilException: NamingException encountered in ldapAdd [LDAP: error code 68 – Object alre
ady exists]
at oracle.ldap.util.Util.ldapAdd(Util.java:2016)
at oracle.ldap.util.Subscriber.createUser(Subscriber.java:1392)
at oracle.ldap.util.Subscriber.createUser(Subscriber.java:1315)
at NewUser.main(NewUser.java:46)

Using the Native Java JNDI Packages

The OID Java API documentation shows how to search and modify entries within an OID ldap server but does not give any examples or instructions for how to create a new entry in the ldap server. This example is very similar to the above java code but uses ONLY native java packages.

The sample code below uses the Java JNDI packages to bind to the OID directory server and then create a new user under a specific location in the directory tree. This code uses NO Oracle-specific Java function calls. The sample code uses only Java packages that are supplied by Sun’s Java Development Kit.

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.*;
import java.util.*;
 
public class NewUser
{
final static String ldapServerName = "mlc2.acme.org";
final static String ldapServerPort = "4032";
final static String rootdn = "cn=orcladmin";
final static String rootpass = "welcome1";
final static String entryDn = "cn=javauser,cn=users,dc=acme,dc=org";
 
public static void main(String argv[]) throws NamingException
{
Properties env = new Properties();
env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
env.put( Context.PROVIDER_URL, "ldap://" + ldapServerName + ":" + ldapServerPort + "/");
env.put( Context.SECURITY_PRINCIPAL, rootdn );
env.put( Context.SECURITY_CREDENTIALS, rootpass );
DirContext ctx = new InitialDirContext(env);
 
// Create the objclassSet to hold all the entry's objectClasses.
BasicAttribute objclassSet = new BasicAttribute("objectclass");
objclassSet.add("person");
objclassSet.add("organizationalPerson");
objclassSet.add("inetOrgPerson");
objclassSet.add("top");
objclassSet.add("orcluser");
objclassSet.add("orcluserv2");
 
// load the attributes
BasicAttributes attrs = new BasicAttributes();
attrs.put(objclassSet);
attrs.put("mail", "[email protected]");
attrs.put("cn", "javauser");
attrs.put("sn", "Campbell");
attrs.put("givenname", "Mike");
attrs.put("uid", "javauser");
attrs.put("userpassword", "welcome1");
 
//create the user in OID
ctx.createSubcontext(entryDn, attrs);
}
}

To use the above sample code do the following:
1. Save the above text indicated between the begin/end cut lines into a file named NewUser.java. Note that the filename and case are important and must be exactly NewUser.java unless the code has been modified.

2. Locate the Java Development Kit (JDK) on your system. For recent Oracle installations it should have been installed under $ORACLE_HOME/jdk but could be located elsewhere.

3. Compile the NewUser.java file into a binary NewUser.class file using the following command:

/bin/javac NewUser.java

Since the sample code does not contain any Oracle-specific packages in it there is no need to specify any classpath. The above command should complete with no errors or output and should result in a file named NewUser.class being created.

4. Execute the resulting NewUser.class file using the following command:

/bin/java NewUser

Note that this time there is NO extension specified (neither .java nor .class is used) and it must be executed from the same directory where the NewUser.class file is located. Again the file is case-sensitive and must be exactly as listed above.

If the NewUser.class file executes successfully there will be no output written to the screen. The user will be created within OID in the location specified by the entryDN variable. This user can then been seen via an ldapsearch or through Oracle Directory Manager (ODM).

If any errors are encountered (such as invalid user/pass, duplicate entry, etc… then a Java exception will occur and a stack trace will be displayed showing the error. For example, if the user already existed then the following error will be displayed: