Reputation: 206
OK, I am adding a couple of custom claims to the payload when I generate the JWT, and I can pull those out just fine in my front-end (javascript). I then have my javascript send an ajax call to a micro-service and it passes the JWT along with it. I want to get my custom claims out of the JWT in the micro-service. I'm doing the following:
Claims claims = Jwts.parser().setSigningKey(Vars.SECRET_KEY).parseClaimsJws(token).getBody();
User user = claims.get("customuser", User.class);
and it throws an exception.
io.jsonwebtoken.RequiredTypeException: Expected value to be of type: class net.netdatacorp.netdauth.model.User, but was class java.util.LinkedHashMap
at io.jsonwebtoken.impl.DefaultClaims.get(DefaultClaims.java:128)
Here is how the data looks in the JWT inspector on the front-end for my custom claim.
{
jti: "83bffbad-7d36-4370-9332-21a84f2a3dce",
iat: 1498241526,
sub: "test",
iss: "www.test.net",
customuser: {
userId: 1,
userCd: "TMM",
firstNm: "Testy",
lastNm: "McTesty",
userNm: "test",
emailAddress: "[email protected]",
active: true,
createdDt: 1491355712000,
createdByUserId: 0,
lastUpdateDt: 1498199278000,
lastUpdateByUserId: 0,
lastLoginDt: 1484928016000
}
}
What am I missing to be able to pull my custom claim out?
Upvotes: 5
Views: 23304
Reputation: 1156
Add Custom Claims to JWT.
Note: I used this in Spring Security
Reserved Claims
Adding Custom Claims
String token = Jwts.builder()
.setSubject(subject)
.setExpiration(expDate)
.claim("userId", "3232")
.claim("UserRole", "Admin")
.signWith(SignatureAlgorithm.HS512, secret )
.compact();
Retrieving Custom Claims
Claims claims = Jwts.parser()
.setSigningKey(tokenSecret)
.parseClaimsJws(jwt).getBody();
// Reading Reserved Claims
System.out.println("Subject: " + claims.getSubject());
System.out.println("Expiration: " + claims.getExpiration());
// Reading Custom Claims
System.out.println("userId: " + claims.get("userId"));
System.out.println("userRole: " + claims.get("userRole"));
Upvotes: 3
Reputation: 3799
I know that your main target is Customer
object. other data already exist in the object of the claim. you can easily manage your own object like this.
@Data //insted of this annotation, you can generate the getters and setters
@JsonIgnoreProperties(ignoreUnknown = true)
public class Customer {
private Integer userId;
private String userCd;
private String firstNm;
........
}
JsonIgnoreProperties
annotation is very important when converts to the object from the token body. it ignores the other Properties the object hasn't. (Jti,Lat,Iss)
now you have the object that you want. let's generate the token.
Map<String, Object> claims = new HashMap<>(); //create a hashmap
Customer customer= new Customer(); //create your object
//assign the initial customer data
customer.setUserId(1);
customer.setUserCd("TMM");
customer.setFirstNm("Testy");
ObjectMapper oMapper = new ObjectMapper(); //create a objectmapper object
Map<String, Object> customerData = oMapper.convertValue(customer, Map.class); //convert the customer object into map of (String, Object)
claims.putAll(customerData ); //put all the customer data into claims map
//create the token using another required data
String token = Jwts.builder()
.setClaims(claims) //this our object
.setSubject("test")
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
.signWith(SignatureAlgorithm.HS512, "secret")
.compact();
go to the https://jwt.io/ and put the generated token and see how is it. it will be like this.
{
"sub": "test",
"firstNm": "Testy", //customer data from the object
"exp": 1622862855,
"userId": 1, //customer data from the object
"iat": 1622844855,
"userCd": "TMM" //customer data from the object,
........
}
it contains all the data with your custom customer data too.
now let's decode the token
Jws<Claims> claimsJws = Jwts.parser().setSigningKey("secret").parseClaimsJws(token);
ObjectMapper mapper = new ObjectMapper();
Customer customer = mapper.convertValue(claimsJws.getBody(), Customer.class); //convert the claims body by mentioning the customer object class
System.out.println("customerData = " + customer);
now you can use the customer data object as you want.
**special thing is the @JsonIgnoreProperties(ignoreUnknown = true)
annotation.
Upvotes: 1
Reputation: 19517
JJWT has had this functionality since its 0.11.0 release.
The idea is that a JWT library should not itself undertake marshaling behavior because 1) it's really complex work to be able to handle any ad-hoc data structure (see the JAXB and Jackson codebases as examples) and 2) the JSON marshaller can do it already - there's no point in JJWT re-inventing that wheel.
So, to leverage the marshaller's built-in support, we need to tell it what fields it should look to unmarshall into custom objects so it can do this at parse time. (By the time the JSON is fully parsed, it's already 'too late' when JJWT starts looking at the JWT Map, so we need to ensure the marshaller can do it at parse time).
You do this by telling the marshaller which fields should be converted into custom types, for example, with Jackson:
Jwts.parserBuilder()
.deserializeJsonWith(new JacksonDeserializer(Maps.of("user", User.class).build())) // <-----
.build()
.parseClaimsJwt(aJwtString)
.getBody()
.get("user", User.class) // <-----
For more information, see JJWT's documentation at https://github.com/jwtk/jjwt#parsing-of-custom-claim-types
Upvotes: 1
Reputation: 5950
We can use Jackson's object mapper to convert Claims (which is a Map<String, Object>
) to our custom claim java object.
final ObjectMapper mapper = new ObjectMapper();
Claims jwsMap = Jwts.parser()
.setSigningKey("SECRET")
.parseClaimsJws("jwt")
.getBody();
return mapper.convertValue(jwsMap, MyCustomClaim.class);
Also add that code to try catch to make sure we handle the case of missing/tampered signature.
Upvotes: 5
Reputation: 206
OK, so I switched to using Jose4J instead of JJWT and after working to get every thing working I realized I probably could have done something similar with JJWT. So what I ended up doing was to use Gson to perform a JSON encoding of the Object and the attaching the resulting JSON string as a claim. And so when I wanted to get a custom claim back out, I would extract the claim as a string and the use the Gson library to convert it back to a POJO.
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
JwtConsumer jwtConsumer = getConsumer();
JwtClaims jwtClaims = jwtConsumer.processToClaims(token);
String userStr = jwtClaims.getClaimValue("user", String.class);
User user = gson.fromJson(userStr, User.class);
Upvotes: 0