java.lang.IllegalArgumentException:如果指定的JWT是数字签名的,则必须指定签名密钥
我希望在我的应用程序中实现java.lang.IllegalArgumentException:如果指定的JWT是数字签名的,则必须指定签名密钥,java,jjwt,Java,Jjwt,我希望在我的应用程序中实现JWT,因为我正在对它进行一些研究和开发,参考:。当我试图通过提取索赔集来验证令牌()时,我成功地实现了generateToken()方法。我不明白apiKey.getSecret()是从哪里来的。你能在这方面指导我吗 以下代码仅供参考: public class JJWTDemo { private static final String secret = "MySecrete"; private static String generateTok
JWT
,因为我正在对它进行一些研究和开发,参考:。当我试图通过提取索赔集来验证令牌()时,我成功地实现了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>
io.jsonwebtoken
jjwt
${jjwt.version}
0.7.0
博客文章中的apiKey.getSecret()
引用了分配给Stormpath为每位客户提供的API密钥的安全、随机生成和Base64编码的密钥(如密码)。Stormpath客户使用此API密钥对进入Stormpath REST API的每个请求进行身份验证。因为每个Stormpath客户都有一个API密钥(并且您的应用程序可以访问该密钥),所以API密钥机密是签名和验证特定于您的应用程序的JWT的理想“默认”密钥
如果您没有Stormpath API密钥,任何足够强的安全随机字节数组都可以用于签名和验证JWT
在上面的示例中,以下内容显示为测试密钥:
private static final String secret = "MySecrete";
这不是有效的(符合JWT的)密钥,并且不能用于JWT HMAC算法
JWT RFC要求使用等于或大于散列输出长度的字节数组键长度
这意味着,如果使用HS256、HS384或HS512,则密钥字节数组必须分别为256位(32字节)、384位(48字节)或512位(64字节)。我将在中对此进行更详细的介绍-请参阅其中的数据,以及可以生成符合规范且安全的密钥的MacProvider
示例
基于此,这里是代码示例,重写为a)生成有效密钥和b)将该密钥引用为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编码的字节数组未加密(文本编码!=加密),因此,如果您对密钥字节进行Base64编码,请确保Base64字符串仍处于安全/隐藏状态
最后,上述静态最终常量(命名为
secret
、secretBytes
和base64SecretBytes
)仅用于此简单的测试演示-永远不要将密钥硬编码到源代码中,更不用说使其成为静态常量,因为它们可以轻松反编译。我100%同意Les Hazlewood。但是我们应该始终发送主题
、发行人
和受众
,以确定当前登录用户的更多详细信息。代码可以修改如下:
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.crypto.MacProvider;
public class TokenUtil {
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 subject, String issuer, String audience) {
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)
.setSubject(subject)
.setIssuer(issuer)
.setAudience(audience)
.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());
System.out.println("Not Before : "+claims.getNotBefore());
System.out.println("Audience :: "+claims.getAudience());
}
public static void main(String[] args) {
String token = generateToken("MySubject", "AH", "MyAudience");
System.out.println("TOKEN :: "+token);
verifyToken(token);
}
}
如何使用SHA-512算法为
ECDSA工作?