baseClaims;
private final String valueClaimName;
private final Long expirationSeconds;
private final Long notBeforeSeconds;
private final SignatureAlgorithm signatureAlgorithm;
private final Key signingKey;
/**
* Creates a new instance based on the specified arguments.
*
* {@code baseHeader}
*
* The optional (nullable) {@code baseHeader} argument allows you to supply any base header name/value pairs
* that should be in the JWT before the JWT is constructed. Any header values required by JWT construction
* will overwrite any identically named values in this map. For example, {@code iat} - 'issued at' will always
* be the timestamp when the JWT is constructed, even if {@code baseHeader} contains an {@code iat} member.
*
* A common reason for setting this value would be, for example, to set the identifier of the signing key used
* during signing (via the {@code kid} header). This allows a recipient of the JWT to look up the same signing
* key based on the {@code kid} header so the recipient can verify the signature.
*
* {@code baseClaims}
*
* The optional (nullable) {@code baseClaims} argument allows you to supply any base claims you wish to add as
* JWT claims before the function value's name/value pairs are applied as claims.
*
* For example, consider the following code with a {@code baseClaims} constructor argument:
*
*
* Map<String,Object> baseClaims = new HashMap<>();
* baseClaims.put("iss", "My Company");
* baseClaims.put("aud", "mywebapp.com");
*
* MapToJwtConverter converter = new MapToJwtConverter(baseClaims, null, null, null);
*
*
* If you were to invoke this converter with a Map value:
*
*
* Map<String,Object> value = new HashMap<>();
* value.put("username", "jsmith");
* value.put("email", "[email protected]");
*
* String jwt = converter.apply(value);
*
*
* The resulting JWT claims will be the set union of both maps and look like this:
*
*
* {
* "iss": "My Company",
* "aud": "webapp.com",
* "username": "jsmith",
* "email", "[email protected]"
* }
*
*
* Any name/value pairs in the function value take precedence and will overwrite (replace) any identically
* named pairs from the {@code baseClaims}.
*
* If you don't want the {@code value} name/value pairs to be set as common/top-level claims and instead want
* them to be set as a single claim with nested pairs, specify the {@code valueClaimName} argument.
*
* A {@code null} {@code baseClaims} argument value indicates no base claims need to be represented in the
* resulting JWT.
*
* {@code valueClaimName}
*
* By default, any name/value pairs in the Map value supplied to the {@link #apply(Map) apply} method will be
* merged and potentially overwrite any pairs that might have been set in the {@code baseClaims}.
*
* If you don't want the function value's name/value pairs to be intermixed with any other JWT base claims, you
* can set the {@code valueClaimName} argument and that will be used to set a single top-level JWT claim using
* the entire Map value as the claim value.
*
* For example, consider the following code with a {@code valueClaimName} constructor argument:
*
*
* MapToJwtConverter converter = new MapToJwtConverter(null, "account", null, null, null);
*
*
* If you were to invoke this converter with a Map value:
*
*
* Map<String,Object> value = new HashMap<>();
* value.put("username", "jsmith");
* value.put("email", "[email protected]");
*
* String jwt = converter.apply(value);
*
*
* The resulting JWT claims will include an {@code account} claim with the same value as the function
* argument. The resulting JWT claims would look like this:
*
*
* {
* "iat": "2016-12-15T19:58:55.272Z",
* //other claims added by the JWT building process truncated for brevity...
* "account": {
* "username": "jsmith",
* "email": "[email protected]"
* }
* }
*
*
* The entire map value is 'nested' under a claim name equal to the specified {@code valueClaimName} argument
* value ("account" in this example).
*
* A {@code null} {@code valueClaimName} argument value indicates that any name/value pairs from the function
* value should not be nested, and instead be represented as common/top-level JWT claims.
*
* {@code signatureAlgorithm}
*
* The JWT signature algorithm to use when signing the JWT with the specified {@code signingKey} argument.
*
* A {@code null} value indicates the JWT should not be signed at all.
* WARNING: JWT values in most production environments should usually always be signed to ensure the JWT
* cannot be manipulated after construction. Not signing JWTs usually leads to security risks.
*
* If {@code signatureAlgorithm} is specified (non-null), the {@code signingKey} argument must be specified as
* well.
*
* {@code signingKey}
*
* The signing key to use when signing the JWT based on the specified {@code signatureAlgorithm} argument.
*
* If {@code signatureAlgorithm} is provided, the {@code signingKey} must be provided as well.
*
* @param baseHeader any base name/value pairs to add to the JWT header before constructing the JWT, or
* {@code null} if no base header pairs are desired.
* @param baseClaims any base claims to add to the JWT before the function value is applied as JWT baseClaims,
* or {@code null} if no base claims are desired.
* @param valueClaimName the name of the JWT claim to represent/'wrap' the function value's name/value pairs, or
* {@code null} if any value pairs should be added directly as common/top-level JWT claims.
* @param signatureAlgorithm the signature algorithm to use when signing the key or {@code null} if the JWT should
* not be signed.
* @param signingKey the signing key to use with the specified {@code signatureAlgorithm} or {@code null} if the
* JWT should not be signed.
* @param expirationSeconds the number of seconds to add to the JWT's creation timestamp, the resulting value of
* which will be used to set the {@code exp} Date claim. A {@code null} value indicates
* that the {@code exp} claim will not be set.
* @param notBeforeSeconds the number of seconds to add (or subtract) to the JWT's creation timestamp, the
* resulting value of which will be used to set the {@code nbf} Date claim. A {@code null}
* value indicates that the {@code nbf} claim will not be set.
*/
public MapToJwtConverter(Map baseHeader, Map baseClaims, String valueClaimName,
SignatureAlgorithm signatureAlgorithm, Key signingKey, Long expirationSeconds,
Long notBeforeSeconds) {
this.valueClaimName = valueClaimName;
if (baseHeader == null) {
this.baseHeader = java.util.Collections.emptyMap();
} else {
this.baseHeader = baseHeader;
}
if (baseClaims == null) {
this.baseClaims = java.util.Collections.emptyMap();
} else {
this.baseClaims = baseClaims;
}
this.expirationSeconds = expirationSeconds;
this.notBeforeSeconds = notBeforeSeconds;
this.signatureAlgorithm = signatureAlgorithm;
this.signingKey = signingKey;
if (signatureAlgorithm != null) {
Assert.notNull(signingKey, "A signing Key argument is required when specifying a SignatureAlgorithm.");
} else if (signingKey != null) {
String msg = "A SignatureAlgorithm argument is required when specifying a signing Key.";
throw new IllegalArgumentException(msg);
}
}
@SuppressWarnings("unchecked")
@Override
public String apply(Map value) {
JwtBuilder builder = Jwts.builder();
if (!Collections.isEmpty(baseHeader)) {
builder.setHeader((Map) baseHeader);
}
if (!Collections.isEmpty(baseClaims)) {
builder.setClaims((Map) baseClaims);
}
if (!Collections.isEmpty(value)) {
if (valueClaimName != null) {
builder.claim(valueClaimName, value);
} else {
for (Map.Entry entry : value.entrySet()) {
builder.claim(entry.getKey(), entry.getValue());
}
}
}
Date now = new Date();
builder.setIssuedAt(now);
long nowMillis = now.getTime();
if (this.expirationSeconds != null) {
long expMillis = nowMillis + (expirationSeconds * 1000);
Date exp = new Date(expMillis);
builder.setExpiration(exp);
}
if (this.notBeforeSeconds != null) {
long nbfMillis = nowMillis + (notBeforeSeconds * 1000);
Date nbf = new Date(nbfMillis);
builder.setNotBefore(nbf);
}
if (signatureAlgorithm != null) {
Assert.notNull(signingKey, "Illegal state: signingKey cannot be null if signatureAlgorithm exists.");
builder.signWith(signatureAlgorithm, signingKey);
}
return builder.compact();
}
}