【应用安全】 使用Java创建和验证JWT

Java对JWT(JSON Web Tokens)的支持过去需要大量的工作:广泛的自定义,几小时的解析依赖关系,以及仅用于组装简单JWT的代码页。不再!

本教程将向您展示如何使用现有的JWT库来做两件事:

  • 生成JWT
  • 解码并验证JWT

您会注意到该教程非常简短。那是因为它很容易。如果您想深入挖掘,请查看JWT规范或深入了解有关在Spring Boot应用程序中使用JWT进行令牌身份验证的更长篇文章。

什么是JWT?

JSON Web令牌是用于以紧凑和安全的方式在各方之间发送信息的JSON对象。 JSON规范或Javascript Object Notation定义了一种使用键值对创建纯文本对象的方法。它是构建基于原始类型(数字,字符串等)的数据的紧凑方式。你可能已经非常熟悉JSON了。它就像没有所有括号的XML。

令牌可用于在各方之间发送任意状态。通常这里“聚会”表示客户端Web应用程序和服务器。 JWT有许多用途:身份验证机制,URL安全编码,安全共享私有数据,互操作性,数据到期等。

实际上,这些信息通常涉及两件事:授权和会话状态。服务器可以使用JWT告诉客户端应用程序允许用户执行哪些操作(或允许他们访问哪些数据)。

JWT通常还用于存储Web会话的依赖于状态的用户数据。因为JWT在客户端应用程序和服务器之间来回传递,这意味着状态数据不必存储在某个数据库中(并随后在每个请求中检索);因此,它可以很好地扩展。

让我们来看一个示例JWT(取自jsonwebtoken.io)

【应用安全】 使用Java创建和验证JWT

 

JWT有三个部分:标题,正文和签名。标题包含有关如何编码JWT的信息。身体是令牌的肉(声称存在的地方)。签名提供安全性。

关于如何编码令牌以及如何将信息存储在正文中,我们将不会详细介绍这些细节。如果需要,请查看前面提到的教程。

不要忘记:加密签名不​​提供机密性;它们只是一种检测篡改JWT的方法,除非JWT是专门加密的,否则它们是公开可见的。签名只是提供了一种验证内容的安全方法。

大。得到它了?现在你需要用JJWT制作一个令牌!在本教程中,我们使用的是现有的JWT库。 Java JWT(a.k.a.,JJWT)由Les Hazlewood创建(Apache Shiro的前任提交者,Stormpath的前联合创始人兼首席技术官,目前是Okta自己的高级架构师),JJWT是一个简化JWT创建和验证的Java库。它完全基于JWT,JWS,JWE,JWK和JWA RFC规范以及Apache 2.0许可条款下的开源。该库还为规范添加了一些不错的功能,例如JWT压缩和声明实施。

用Java生成令牌

这部分超级简单。我们来看一些代码吧。克隆GitHub仓库:

git clone https://github.com/oktadeveloper/okta-java-jwt-example.git 
 cd okta-java-jwt-example

这个例子非常基本,包含一个带有两个静态方法的src / main / java / JWTDemo.java类文件:createJWT()和decodeJWT()。 狡猾的是,这两种方法创建了JWT并解码了JWT。 看看下面的第一种方法。

public static String createJWT(String id, String issuer, String subject, long ttlMillis) {
 
 //The JWT signature algorithm we will be using to sign the token
 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
 long nowMillis = System.currentTimeMillis();
 Date now = new Date(nowMillis);
 //We will sign our JWT with our ApiKey secret
 byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET_KEY);
 Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
 //Let's set the JWT Claims
 JwtBuilder builder = Jwts.builder().setId(id)
 .setIssuedAt(now)
 .setSubject(subject)
 .setIssuer(issuer)
 .signWith(signatureAlgorithm, signingKey);
 
 //if it has been specified, let's add the expiration
 if (ttlMillis > 0) {
 long expMillis = nowMillis + ttlMillis;
 Date exp = new Date(expMillis);
 builder.setExpiration(exp);
 } 
 
 //Builds the JWT and serializes it to a compact, URL-safe string
 return builder.compact();
}

总而言之,createJWT()方法执行以下操作:

  1. 设置散列算法
  2. 获取Issued At声明的当前日期
  3. 使用SECRET_KEY静态属性生成签名密钥
  4. 使用流畅的API添加声明并签署JWT
  5. 设置到期日期

这可以根据您的需求进行定制。 例如,如果您要添加不同或自定义声明。

解码令牌

现在来看看更简单的decodeJWT()方法。

public static Claims decodeJWT(String jwt) {
 //This line will throw an exception if it is not a signed JWS (as expected)
 Claims claims = Jwts.parser()
 .setSigningKey(DatatypeConverter.parseBase64Binary(SECRET_KEY))
 .parseClaimsJws(jwt).getBody();
 return claims;
}

该方法再次使用静态SECRET_KEY属性生成签名密钥,并使用它来验证JWT是否未被篡改。 如果签名与令牌不匹配,则该方法将抛出io.jsonwebtoken.SignatureException异常。 如果签名匹配,则该方法将声明作为声明对象返回。

这就是它!

运行JUnit测试

为了额外的功劳,您可以在示例项目中运行JUnit测试。 有三个测试,它们展示了JJWT库的一些基本功能。 第一个测试显示了快乐路径,创建并成功解码了有效的JWT。 第二个测试显示当您尝试将完全伪造的字符串解码为JWT时JJWT库将如何失败。 最后一个测试显示了被篡改的JJWT将如何导致decodeJWT()方法抛出SignatureException。

您可以使用以下命令从命令行运行这些测试:

./gradlew test -i

-i是将Gradle的日志级别设置为Info,以便我们从测试中看到简单的日志记录输出。

了解有关在Java应用程序中使用JWT的更多信息

JJWT库使得创建和验证JWT变得非常容易。只需指定一个密钥和一些声明,你就有了一个JJWT。稍后,使用相同的密钥对JJWT进行解码并验证其内容。

创建和使用JJWT现在非常简单,为什么不使用它们?

不要忘记SSL!请记住,除非JWT加密,否则其中编码的信息通常只有Base64编码,任何小孩和一些宠物都可以阅读。因此,除非您希望中国,俄罗斯和FBI读取您的所有会话数据,否则请使用SSL对其进行加密。

Baeldung在Java和JWT方面有很好的深度教程。

此外,以下是来自Okta博客的更多链接,以便您继续:

  • Java应用程序的简单令牌认证
  • 开始使用Spring Boot,OAuth 2.0和Okta
  • 10种保护Spring Boot应用程序的绝佳方法
  • 如果您的JWT被盗,会发生什么?
  • JWT分析仪和Inspector Chrom插件
  • 在线编码或解码JWT

 

本文:https://pub.intelligentx.net/application-security-create-and-validate-jwt-using-java

讨论:请加入知识星球或者小红圈【首席架构师圈】