de.cidaas.jwt.JWTCreator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cidaas-interceptor-spring-security Show documentation
Show all versions of cidaas-interceptor-spring-security Show documentation
Interceptor for Cidaas Java Spring Clients
package de.cidaas.jwt;
import de.cidaas.jwt.algorithms.Algorithm;
import de.cidaas.jwt.exceptions.JWTCreationException;
import de.cidaas.jwt.exceptions.SignatureGenerationException;
import de.cidaas.jwt.impl.ClaimsHolder;
import de.cidaas.jwt.impl.PayloadSerializer;
import de.cidaas.jwt.impl.PublicClaims;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.apache.commons.codec.binary.Base64;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* The JWTCreator class holds the sign method to generate a complete JWT (with Signature) from a given Header and Payload content.
*/
@SuppressWarnings("WeakerAccess")
public final class JWTCreator {
private final Algorithm algorithm;
private final String headerJson;
private final String payloadJson;
private JWTCreator(Algorithm algorithm, Map headerClaims, Map payloadClaims) throws JWTCreationException {
this.algorithm = algorithm;
try {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(ClaimsHolder.class, new PayloadSerializer());
mapper.registerModule(module);
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
headerJson = mapper.writeValueAsString(headerClaims);
payloadJson = mapper.writeValueAsString(new ClaimsHolder(payloadClaims));
} catch (JsonProcessingException e) {
throw new JWTCreationException("Some of the Claims couldn't be converted to a valid JSON format.", e);
}
}
/**
* Initialize a JWTCreator instance.
*
* @return a JWTCreator.Builder instance to configure.
*/
static JWTCreator.Builder init() {
return new Builder();
}
/**
* The Builder class holds the Claims that defines the JWT to be created.
*/
public static class Builder {
private final Map payloadClaims;
private Map headerClaims;
Builder() {
this.payloadClaims = new HashMap();
this.headerClaims = new HashMap();
}
/**
* Add specific Claims to set as the Header.
*
* @param headerClaims the values to use as Claims in the token's Header.
* @return this same Builder instance.
*/
public Builder withHeader(Map headerClaims) {
this.headerClaims = new HashMap(headerClaims);
return this;
}
/**
* Add a specific Key Id ("kid") claim to the Header.
* If the {@link Algorithm} used to sign this token was instantiated with a KeyProvider, the 'kid' value will be taken from that provider and this one will be ignored.
*
* @param keyId the Key Id value.
* @return this same Builder instance.
*/
public Builder withKeyId(String keyId) {
this.headerClaims.put(PublicClaims.KEY_ID, keyId);
return this;
}
/**
* Add a specific Issuer ("iss") claim to the Payload.
*
* @param issuer the Issuer value.
* @return this same Builder instance.
*/
public Builder withIssuer(String issuer) {
addClaim(PublicClaims.ISSUER, issuer);
return this;
}
/**
* Add a specific Subject ("sub") claim to the Payload.
*
* @param subject the Subject value.
* @return this same Builder instance.
*/
public Builder withSubject(String subject) {
addClaim(PublicClaims.SUBJECT, subject);
return this;
}
/**
* Add a specific Audience ("aud") claim to the Payload.
*
* @param audience the Audience value.
* @return this same Builder instance.
*/
public Builder withAudience(String... audience) {
addClaim(PublicClaims.AUDIENCE, audience);
return this;
}
/**
* Add a specific Expires At ("exp") claim to the Payload.
*
* @param expiresAt the Expires At value.
* @return this same Builder instance.
*/
public Builder withExpiresAt(Date expiresAt) {
addClaim(PublicClaims.EXPIRES_AT, expiresAt);
return this;
}
/**
* Add a specific Not Before ("nbf") claim to the Payload.
*
* @param notBefore the Not Before value.
* @return this same Builder instance.
*/
public Builder withNotBefore(Date notBefore) {
addClaim(PublicClaims.NOT_BEFORE, notBefore);
return this;
}
/**
* Add a specific Issued At ("iat") claim to the Payload.
*
* @param issuedAt the Issued At value.
* @return this same Builder instance.
*/
public Builder withIssuedAt(Date issuedAt) {
addClaim(PublicClaims.ISSUED_AT, issuedAt);
return this;
}
/**
* Add a specific JWT Id ("jti") claim to the Payload.
*
* @param jwtId the Token Id value.
* @return this same Builder instance.
*/
public Builder withJWTId(String jwtId) {
addClaim(PublicClaims.JWT_ID, jwtId);
return this;
}
/**
* Add a custom Claim value.
*
* @param name the Claim's name.
* @param value the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withClaim(String name, Boolean value) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, value);
return this;
}
/**
* Add a custom Claim value.
*
* @param name the Claim's name.
* @param value the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withClaim(String name, Integer value) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, value);
return this;
}
/**
* Add a custom Claim value.
*
* @param name the Claim's name.
* @param value the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withClaim(String name, Long value) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, value);
return this;
}
/**
* Add a custom Claim value.
*
* @param name the Claim's name.
* @param value the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withClaim(String name, Double value) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, value);
return this;
}
/**
* Add a custom Claim value.
*
* @param name the Claim's name.
* @param value the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withClaim(String name, String value) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, value);
return this;
}
/**
* Add a custom Claim value.
*
* @param name the Claim's name.
* @param value the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withClaim(String name, Date value) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, value);
return this;
}
/**
* Add a custom Array Claim with the given items.
*
* @param name the Claim's name.
* @param items the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withArrayClaim(String name, String[] items) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, items);
return this;
}
/**
* Add a custom Array Claim with the given items.
*
* @param name the Claim's name.
* @param items the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withArrayClaim(String name, Integer[] items) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, items);
return this;
}
/**
* Add a custom Array Claim with the given items.
*
* @param name the Claim's name.
* @param items the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withArrayClaim(String name, Long[] items) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, items);
return this;
}
/**
* Creates a new JWT and signs is with the given algorithm
*
* @param algorithm used to sign the JWT
* @return a new JWT token
* @throws IllegalArgumentException if the provided algorithm is null.
* @throws JWTCreationException if the claims could not be converted to a valid JSON or there was a problem with the signing key.
*/
public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCreationException {
if (algorithm == null) {
throw new IllegalArgumentException("The Algorithm cannot be null.");
}
headerClaims.put(PublicClaims.ALGORITHM, algorithm.getName());
headerClaims.put(PublicClaims.TYPE, "JWT");
String signingKeyId = algorithm.getSigningKeyId();
if (signingKeyId != null) {
withKeyId(signingKeyId);
}
return new JWTCreator(algorithm, headerClaims, payloadClaims).sign();
}
private void assertNonNull(String name) {
if (name == null) {
throw new IllegalArgumentException("The Custom Claim's name can't be null.");
}
}
private void addClaim(String name, Object value) {
if (value == null) {
payloadClaims.remove(name);
return;
}
payloadClaims.put(name, value);
}
}
private String sign() throws SignatureGenerationException {
String header = Base64.encodeBase64URLSafeString(headerJson.getBytes(StandardCharsets.UTF_8));
String payload = Base64.encodeBase64URLSafeString(payloadJson.getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = algorithm.sign(header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8));
String signature = Base64.encodeBase64URLSafeString((signatureBytes));
return String.format("%s.%s.%s", header, payload, signature);
}
}