3

好的,我在生成 JWT 时向有效负载添加了几个自定义声明,我可以在前端 (javascript) 中很好地提取这些声明。然后我让我的 javascript 向一个微服务发送一个 ajax 调用,它会同时传递 JWT。我想从微服务中的 JWT 中获取我的自定义声明。我正在执行以下操作:

Claims claims = Jwts.parser().setSigningKey(Vars.SECRET_KEY).parseClaimsJws(token).getBody();
 User user = claims.get("customuser", User.class);

它抛出一个异常。

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)

这是我的自定义声明在前端的 JWT 检查器中的数据外观。

{
  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: "jacob@test.net",
    active: true,
    createdDt: 1491355712000,
    createdByUserId: 0,
    lastUpdateDt: 1498199278000,
    lastUpdateByUserId: 0,
    lastLoginDt: 1484928016000
  }
}

我缺少什么能够提取我的自定义声明?

4

5 回答 5

5

我们可以使用 Jackson 的对象映射器将 Claims(它是 a Map<String, Object>)转换为我们自定义的 claim java 对象。

final ObjectMapper mapper = new ObjectMapper();

Claims jwsMap = Jwts.parser()
       .setSigningKey("SECRET")
       .parseClaimsJws("jwt")
       .getBody();
return mapper.convertValue(jwsMap, MyCustomClaim.class);

还要添加该代码以尝试捕获以确保我们处理丢失/篡改签名的情况。

于 2018-06-02T12:41:06.397 回答
0

好的,所以我改用 Jose4J 而不是 JJWT,在努力让每件事都正常工作之后,我意识到我可能可以用 JJWT 做类似的事情。所以我最终做的是使用 Gson 对 Object 执行 JSON 编码,并将生成的 JSON 字符串作为声明附加。因此,当我想恢复自定义声明时,我会将声明提取为字符串,并使用 Gson 库将其转换回 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);
于 2017-07-07T04:30:54.600 回答
0

JJWT 自 0.11.0 发布以来就具有此功能。

这个想法是 JWT 库本身不应该进行编组行为,因为 1)能够处理任何临时数据结构(参见 JAXB 和 Jackson 代码库作为示例)是非常复杂的工作,并且 2)JSON 编组器可以做到这一点已经 - JJWT 重新发明那个轮子没有意义。

因此,为了利用编组器的内置支持,我们需要告诉它应该将哪些字段解组为自定义对象,以便它可以在解析时执行此操作。(当 JSON 被完全解析时,当 JJWT 开始查看 JWT Map 时已经“为时已晚”,因此我们需要确保编组器可以在解析时完成)。

您可以通过告诉编组器哪些字段应该转换为自定义类型来做到这一点,例如,对于 Jackson:

Jwts.parserBuilder()

    .deserializeJsonWith(new JacksonDeserializer(Maps.of("user", User.class).build())) // <-----

    .build()

    .parseClaimsJwt(aJwtString)

    .getBody()
    
    .get("user", User.class) // <-----

有关更多信息,请参阅https://github.com/jwtk/jjwt#parsing-of-custom-claim-types上的 JJWT 文档

于 2021-03-02T04:09:26.203 回答
0

我知道您的主要目标是Customer对象。其他数据已经存在于索赔的对象中。您可以像这样轻松管理自己的对象。

@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从token体转换为对象时,注解非常重要。它忽略了对象没有的其他属性。(Jti,纬度,ISS)

现在你有了你想要的对象。让我们生成令牌。

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();


https://jwt.io/把生成的token放进去看看怎么样。会是这样。

{
  "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,
  ........
}

它也包含所有数据以及您的自定义客户数据。

现在让我们解码令牌

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);

现在您可以根据需要使用客户数据对象。**特别的是@JsonIgnoreProperties(ignoreUnknown = true)注释。

于 2021-06-04T22:26:40.760 回答
0

将自定义声明添加到 JWT。

注意:我在 Spring Security 中使用了这个

保留索赔

  • iss – 发行人
  • 子 - 主题
  • aud – 观众
  • exp – 到期
  • nbf – 不是以前
  • iat——发布于
  • jti——智威汤逊ID

添加自定义声明

String token = Jwts.builder()
.setSubject(subject)
.setExpiration(expDate)
.claim("userId", "3232")
.claim("UserRole", "Admin")
.signWith(SignatureAlgorithm.HS512, secret )
.compact();

检索自定义声明

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"));
于 2021-06-28T12:47:15.430 回答