我希望在我的应用程序中实现 JWT,因为我正在通过引用以下内容对其进行一些研发:https://stormpath.com/blog/jwt-java-create-verify .当我尝试通过提取声明集来 verifyToken() 时,我成功地实现了 generateToken() 方法。我不明白 apiKey.getSecret() 是从哪里来的。你能指导我吗?
以下代码供引用:
public class JJWTDemo {
private static final String secret = "MySecrete";
private static String generateToken(){
String id = UUID.randomUUID().toString().replace("-", "");
Date now = new Date();
Date exp = new Date(System.currentTimeMillis() + (1000*30)); // 30 seconds
String token = Jwts.builder()
.setId(id)
.setIssuedAt(now)
.setNotBefore(now)
.setExpiration(exp)
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
return token;
}
private static void verifyToken(String token){
Claims claims = Jwts.parser().
setSigningKey(DatatypeConverter.parseBase64Binary(apiKey.getSecret()))
.parseClaimsJws(token).getBody();
System.out.println("----------------------------");
System.out.println("ID: " + claims.getId());
System.out.println("Subject: " + claims.getSubject());
System.out.println("Issuer: " + claims.getIssuer());
System.out.println("Expiration: " + claims.getExpiration());
}
public static void main(String[] args) {
System.out.println(generateToken());
String token = generateToken();
verifyToken(token);
}
}
我看到下面的错误来了:
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4N2E5NmYwNTcyN2M0ZDY0YjZmODlhNDAyOTQ2OTZiNyIsImlhdCI6MTQ4NDQ4NjYyNiwibmJmIjoxNDg0NDg2NjI2LCJleHAiOjE0ODQ0ODY2NTZ9.ycS7nLWnPpe28DM7CcQYBswOmMUhBd3wQwfZ9C-yQYs
Exception in thread "main" java.lang.IllegalArgumentException: A signing key must be specified if the specified JWT is digitally signed.
at io.jsonwebtoken.lang.Assert.notNull(Assert.java:85)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:331)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481)
at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541)
at io.jsonwebtoken.jjwtfun.service.JJWTDemo.verifyToken(JJWTDemo.java:31)
at io.jsonwebtoken.jjwtfun.service.JJWTDemo.main(JJWTDemo.java:41)
Maven 依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
<jjwt.version>0.7.0</jjwt.version>
最佳答案
博客文章中的
apiKey.getSecret() 是对分配给 Stormpath 为每个客户提供的 API key 的安全、随机生成和 Base64 编码的 key (如密码)的引用. Stormpath 客户使用此 API key 对进入 Stormpath REST API 的每个请求进行身份验证。因为每个 Stormpath 客户都有一个 API key (并且您的应用程序可以访问该 key ),API key secret 是签署和验证特定于您的应用程序的 JWT 的理想“默认值”。
如果您没有 Stormpath API key ,任何足够强大的安全随机字节数组都可以用于签署和验证 JWT。
在您上面的示例中,以下内容显示为测试 key :
private static final String secret = "MySecrete";
这不是有效的(符合 JWT 标准的) key ,不能用于 JWT HMAC 算法。
JWT RFC 要求您 MUST使用长度等于或大于散列输出长度的字节数组键。
这意味着如果您使用 HS256、HS384 或 HS512,您的 key 字节数组必须分别为 256 位(32 字节)、384 位(48 字节)或 512 位(64 字节)。我在 another StackOverflow answer 中对此进行了更详细的介绍。 - 查看那里的数据,以及可以为您生成符合规范的安全 key 的 MacProvider 示例。
基于此,这里是代码示例,重写为 a) 生成有效 key 和 b) 将该 key 引用为 Base64 编码的字符串:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.crypto.MacProvider;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
public class JJWTDemo {
private static final Key secret = MacProvider.generateKey(SignatureAlgorithm.HS256);
private static final byte[] secretBytes = secret.getEncoded();
private static final String base64SecretBytes = Base64.getEncoder().encodeToString(secretBytes);
private static String generateToken() {
String id = UUID.randomUUID().toString().replace("-", "");
Date now = new Date();
Date exp = new Date(System.currentTimeMillis() + (1000 * 30)); // 30 seconds
String token = Jwts.builder()
.setId(id)
.setIssuedAt(now)
.setNotBefore(now)
.setExpiration(exp)
.signWith(SignatureAlgorithm.HS256, base64SecretBytes)
.compact();
return token;
}
private static void verifyToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(base64SecretBytes)
.parseClaimsJws(token).getBody();
System.out.println("----------------------------");
System.out.println("ID: " + claims.getId());
System.out.println("Subject: " + claims.getSubject());
System.out.println("Issuer: " + claims.getIssuer());
System.out.println("Expiration: " + claims.getExpiration());
}
public static void main(String[] args) {
System.out.println(generateToken());
String token = generateToken();
verifyToken(token);
}
}
请注意,Base64 编码的字节数组未加密(文本编码!= 加密),因此请确保如果您对您的 key 字节进行 Base64 编码,您仍然保持该 Base64 字符串安全/隐藏.
最后,上面的静态最终常量(名为 secret、secretBytes 和 base64SecretBytes)仅用于这个简单的测试演示 - 应该永远不要将 key 硬编码到源代码中,更不用说将它们设为静态常量了,因为它们很容易被反编译。
关于java.lang.IllegalArgumentException : A signing key must be specified if the specified JWT is digitally signed 异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41661821/