我正在寻求JWT
在我的应用程序中实现,我正在通过以下参考进行一些研发:https://stormpath.com/blog/jwt-java-create-verify.generateToken()
当我尝试verifyToken()
通过提取声明集时,我成功地能够实现该方法.我不明白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依赖:
io.jsonwebtoken jjwt ${jjwt.version} 0.7.0
Les Hazlewoo.. 7
apiKey.getSecret()
在博客文章中引用了分配给Stormpath为每个客户提供的API密钥的安全,随机生成的&Base64编码的密钥(如密码).Stormpath客户使用此API密钥对Stormpath REST API中的每个请求进行身份验证.由于每个Stormpath客户都有一个API密钥(并且密钥可供您的应用程序访问),因此API Key密钥是签名和验证特定于您的应用程序的JWT的理想"默认".
如果您没有Stormpath API密钥,任何足够强大的安全随机字节数组都可以用于签名和验证JWT.
在上面的示例中,以下内容显示为测试键:
private static final String secret = "MySecrete";
这不是有效的(符合JWT的)密钥,也不能用于JWT HMAC算法.
JWT RFC 要求您必须使用等于或大于散列输出长度的字节数组密钥长度.
这意味着如果使用HS256,HS384或HS512,则关键字节数组必须分别为256位(32字节),384位(48字节)或512位(64字节).我在另一个StackOverflow答案中详细介绍了这一点- 查看那里的数据,以及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
)在那里为只有这个简单的试验示范-一个永远不应该硬编码到钥匙的源代码,更不用说让他们静态常量,因为它们很容易被反编译.
apiKey.getSecret()
在博客文章中引用了分配给Stormpath为每个客户提供的API密钥的安全,随机生成的&Base64编码的密钥(如密码).Stormpath客户使用此API密钥对Stormpath REST API中的每个请求进行身份验证.由于每个Stormpath客户都有一个API密钥(并且密钥可供您的应用程序访问),因此API Key密钥是签名和验证特定于您的应用程序的JWT的理想"默认".
如果您没有Stormpath API密钥,任何足够强大的安全随机字节数组都可以用于签名和验证JWT.
在上面的示例中,以下内容显示为测试键:
private static final String secret = "MySecrete";
这不是有效的(符合JWT的)密钥,也不能用于JWT HMAC算法.
JWT RFC 要求您必须使用等于或大于散列输出长度的字节数组密钥长度.
这意味着如果使用HS256,HS384或HS512,则关键字节数组必须分别为256位(32字节),384位(48字节)或512位(64字节).我在另一个StackOverflow答案中详细介绍了这一点- 查看那里的数据,以及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
)在那里为只有这个简单的试验示范-一个永远不应该硬编码到钥匙的源代码,更不用说让他们静态常量,因为它们很容易被反编译.