如何在 JJWT (Jackson) 中向 JWT 令牌添加空声明

问题描述 投票:0回答:1

我正在尝试设置一堆声明,其中许多包含空值。这些属性必须存在,并且是用户在客户端应用程序中不被拒绝所必需的,即使它们为空。 问题是默认情况下 JJWT 不将 null 值包含到 JWT 中(或者至少看起来是这样)。尽管如此,内部序列化对象确实包含 null 属性。

这就是我添加它们的方式,以及我尝试实现的解决方案,即配置 ObjectMapper 并在 JWT 构建器中使用它。

public String generateTokenFromJwtUserDetails(JwtUserDetails userDetails) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + jwtExpiration);

        Map<String, Object> claims = new HashMap<>();
        claims.put("name", userDetails.getName()); // null, will not appear in jwt
        claims.put("id", userDetails.getId()); // not null, appears
        claims.put("tenant", userDetails.getTenant()); // contains null values that do end up in jwt

        // rest of claims...

        // here I tried to set serialization inclusion in a custom object mapper, doesn't work
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);

        // I also tried this, but won't work either
        //objectMapper.enable(SerializationFeature.WRITE_NULL_MAP_VALUES);

        try{
            return Jwts.builder()
                    .json(new JacksonSerializer<>(objectMapper))
                    .claims(claims)
                    .subject(userDetails.getUsername())
                    .issuedAt(now)
                    .expiration(expiryDate)
                    .signWith(getSignInKey())
                    .issuer(jwtIssuer)
                    .compact();
        }catch(Exception e){ // ...
}

进一步说明:

  1. 我还在我的 JWT 中包含实体,这些实体由 jackson 及其所有字段进行序列化(因此我嵌套了具有空值的对象,但外部 JWT 不接受它们)。例如:
{
  "id": "ibn12u3bio412i",
  "tenant": {
    "deletedOn": null,
  }
}
  1. HashMap 确实包含所有键和值,即使为 null 时也是如此。
  2. 我知道 JWT 不应包含空数据,但在这种情况下我无法控制客户端。

感谢您的宝贵时间!

java spring-security jwt jackson-databind jjwt
1个回答
0
投票

JJWT 使用自己的内部逻辑来确定是否序列化或排除声明中的空值。您设置的自定义 ObjectMapper 主要应用于序列化期间的嵌套对象,但不适用于顶级声明映射。

也许这个解决方法会对您有所帮助:

第一: 您需要创建一个自定义 ObjectMapper,就像您已经完成的那样。

objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); 

第二: 连载一下:

String serializedClaims = objectMapper.writeValueAsString(claims);

第三: 将其转换回地图。

Map<String, Object> claimsWithNulls = objectMapper.readValue(serializedClaims, new TypeReference<Map<String, Object>>() {});

第四: 现在,您可以为 jwt 提供带有 null 的地图。

Map<String, Object> claimsWithNulls = objectMapper.readValue(serializedClaims, new TypeReference<Map<String, Object>>() {});

试试这个:

   try {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        String serializedClaims = objectMapper.writeValueAsString(claims);
        Map<String, Object> claimsWithNulls = objectMapper.readValue(serializedClaims, new TypeReference<Map<String, Object>>() {});

        return Jwts.builder()
                .setClaims(claimsWithNulls)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(getSignInKey())
                .setIssuer(jwtIssuer)
                .compact();
    } catch (Exception e) {
        throw new RuntimeException("Error generating token", e);
    }
© www.soinside.com 2019 - 2024. All rights reserved.