net.oauth.jsontoken.JsonToken.java Source code

Java tutorial

Introduction

Here is the source code for net.oauth.jsontoken.JsonToken.java

Source

/**
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package net.oauth.jsontoken;

import com.google.common.base.Preconditions;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;

import net.oauth.jsontoken.crypto.AsciiStringSigner;
import net.oauth.jsontoken.crypto.SignatureAlgorithm;
import net.oauth.jsontoken.crypto.Signer;

import org.apache.commons.codec.binary.Base64;
import org.joda.time.Instant;

import java.security.SignatureException;

/**
 * A JSON Token.
 */
public class JsonToken {
    // header names
    public final static String ALGORITHM_HEADER = "alg";
    public final static String KEY_ID_HEADER = "kid";
    public final static String TYPE_HEADER = "typ";

    // standard claim names (payload parameters)
    public final static String ISSUER = "iss";
    public final static String ISSUED_AT = "iat";
    public final static String EXPIRATION = "exp";
    public final static String AUDIENCE = "aud";

    // default encoding for all Json token
    public final static String BASE64URL_ENCODING = "base64url";

    public final static int DEFAULT_LIFETIME_IN_MINS = 2;

    private JsonObject header;
    private SignatureAlgorithm sigAlg;

    protected final Clock clock;
    private final JsonObject payload;
    private final String tokenString;

    // The following fields are only valid when signing the token.
    private final Signer signer;
    private String signature;
    private String baseString;

    /**
     * Public constructor, use empty data type.
     * @param signer the signer that will sign the token.
     */
    public JsonToken(Signer signer) {
        this(signer, new SystemClock());
    }

    /**
     * Public constructor.
     * @param signer the signer that will sign the token
     * @param clock a clock whose notion of current time will determine the not-before timestamp
     *   of the token, if not explicitly set.
     */
    public JsonToken(Signer signer, Clock clock) {
        Preconditions.checkNotNull(signer);
        Preconditions.checkNotNull(clock);

        this.payload = new JsonObject();
        this.signer = signer;
        this.clock = clock;
        this.sigAlg = signer.getSignatureAlgorithm();
        this.signature = null;
        this.baseString = null;
        this.tokenString = null;
        String issuer = signer.getIssuer();
        if (issuer != null) {
            setParam(JsonToken.ISSUER, issuer);
        }
    }

    /**
     * Public constructor used when parsing a JsonToken {@link JsonToken}
     * (as opposed to create a token). This constructor takes Json payload
     * and clock as parameters, set all other signing related parameters to null.
     *
     * @param payload A payload JSON object.
     * @param clock a clock whose notion of current time will determine the not-before timestamp
     *   of the token, if not explicitly set.
     * @param tokenString The original token string we parsed to get this payload.
     */
    public JsonToken(JsonObject header, JsonObject payload, Clock clock, String tokenString) {
        this.payload = payload;
        this.clock = clock;
        this.baseString = null;
        this.signature = null;
        this.sigAlg = null;
        this.signer = null;
        this.header = header;
        this.tokenString = tokenString;
    }

    /**
     * Public constructor used when parsing a JsonToken {@link JsonToken}
     * (as opposed to create a token). This constructor takes Json payload
     * as parameter, set all other signing related parameters to null.
     *
     * @param payload A payload JSON object.
     */
    public JsonToken(JsonObject payload) {
        this.payload = payload;
        this.baseString = null;
        this.tokenString = null;
        this.signature = null;
        this.sigAlg = null;
        this.signer = null;
        this.clock = null;
    }

    /**
     * Public constructor used when parsing a JsonToken {@link JsonToken}
     * (as opposed to create a token). This constructor takes Json payload
     * and clock as parameters, set all other signing related parameters to null.
     *
     * @param payload A payload JSON object.
     * @param clock a clock whose notion of current time will determine the not-before timestamp
     *   of the token, if not explicitly set.
     */
    public JsonToken(JsonObject payload, Clock clock) {
        this.payload = payload;
        this.clock = clock;
        this.baseString = null;
        this.tokenString = null;
        this.signature = null;
        this.sigAlg = null;
        this.signer = null;
    }

    /**
     * Returns the serialized representation of this token, i.e.,
     * keyId.sig.base64(payload).base64(data_type).base64(encoding).base64(alg)
     *
     * This is what a client (token issuer) would send to a token verifier over the
     * wire.
     * @throws SignatureException if the token can't be signed.
     */
    public String serializeAndSign() throws SignatureException {
        String baseString = computeSignatureBaseString();
        String sig = getSignature();
        return JsonTokenUtil.toDotFormat(baseString, sig);
    }

    /**
     * Returns a human-readable version of the token.
     */
    @Override
    public String toString() {
        return JsonTokenUtil.toJson(payload);
    }

    public String getIssuer() {
        return getParamAsString(ISSUER);
    }

    public Instant getIssuedAt() {
        Long issuedAt = getParamAsLong(ISSUED_AT);
        if (issuedAt == null) {
            return null;
        }
        // JWT represents time in seconds, Instants expect milliseconds
        return new Instant(issuedAt * 1000);
    }

    public void setIssuedAt(Instant instant) {
        setParam(JsonToken.ISSUED_AT, instant.getMillis() / 1000);
    }

    public Instant getExpiration() {
        Long expiration = getParamAsLong(EXPIRATION);
        if (expiration == null) {
            return null;
        }
        // JWT represents time in seconds, Instants expect milliseconds
        return new Instant(expiration * 1000);
    }

    public void setExpiration(Instant instant) {
        setParam(JsonToken.EXPIRATION, instant.getMillis() / 1000);
    }

    public String getAudience() {
        return getParamAsString(AUDIENCE);
    }

    public void setAudience(String audience) {
        setParam(AUDIENCE, audience);
    }

    public void setParam(String name, String value) {
        payload.addProperty(name, value);
    }

    public void setParam(String name, Number value) {
        payload.addProperty(name, value);
    }

    public JsonPrimitive getParamAsPrimitive(String param) {
        JsonElement element = payload.get(param);
        if (element != null && element.isJsonPrimitive()) {
            return (JsonPrimitive) element;
        }
        return null;
    }

    public JsonObject getPayloadAsJsonObject() {
        return payload;
    }

    public String getKeyId() {
        return signer.getKeyId();
    }

    public SignatureAlgorithm getSignatureAlgorithm() {
        if (sigAlg == null) {
            if (header == null) {
                throw new IllegalStateException("JWT has no algorithm or header");
            }
            JsonElement algorithmName = header.get(JsonToken.ALGORITHM_HEADER);
            if (algorithmName == null) {
                throw new IllegalStateException(
                        "JWT header is missing the required '" + JsonToken.ALGORITHM_HEADER + "' parameter");
            }
            sigAlg = SignatureAlgorithm.getFromJsonName(algorithmName.getAsString());
        }
        return sigAlg;
    }

    public String getTokenString() {
        return tokenString;
    }

    public JsonObject getHeader() {
        if (header == null) {
            createHeader();
        }
        return header;
    }

    private String getParamAsString(String param) {
        JsonPrimitive primitive = getParamAsPrimitive(param);
        return primitive == null ? null : primitive.getAsString();
    }

    private Long getParamAsLong(String param) {
        JsonPrimitive primitive = getParamAsPrimitive(param);
        if (primitive != null && (primitive.isNumber() || primitive.isString())) {
            try {
                return primitive.getAsLong();
            } catch (NumberFormatException e) {
                return null;
            }
        }
        return null;
    }

    protected String computeSignatureBaseString() {
        if (baseString != null && !baseString.isEmpty()) {
            return baseString;
        }
        baseString = JsonTokenUtil.toDotFormat(JsonTokenUtil.toBase64(getHeader()),
                JsonTokenUtil.toBase64(payload));
        return baseString;
    }

    private JsonObject createHeader() {
        header = new JsonObject();
        header.addProperty(ALGORITHM_HEADER, getSignatureAlgorithm().getNameForJson());
        String keyId = getKeyId();
        if (keyId != null) {
            header.addProperty(KEY_ID_HEADER, keyId);
        }
        return header;
    }

    private String getSignature() throws SignatureException {
        if (signature != null && !signature.isEmpty()) {
            return signature;
        }

        if (signer == null) {
            throw new SignatureException("can't sign JsonToken with signer.");
        }
        String signature;
        // now, generate the signature
        AsciiStringSigner asciiSigner = new AsciiStringSigner(signer);
        signature = Base64.encodeBase64URLSafeString(asciiSigner.sign(baseString));

        return signature;
    }

}