如何延长Java JSON Web Token的过期时间?

11

我尝试使用jjwt库在Java中创建Json Web Token。

但是当我尝试延长过期时间时,出现了问题。

我尝试通过以下代码实现。

public class Main {
public static void main(String args[]) {
    byte[] key = new byte[64];
    new SecureRandom().nextBytes(key);
    Date date = new Date();
    long t = date.getTime();
    Date expirationTime = new Date(t + 5000l); // set 5 seconds

    String compact = Jwts.builder().setSubject("Joe").setExpiration(expirationTime).signWith(SignatureAlgorithm.HS256, key).compact();
    System.out.println("compact : " + compact);
    try {
        String unpack = Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject();
        System.out.println("unpackage 0 : " + unpack);
        
        // check if the expiration work.
        Thread.sleep(3000);
        System.out.println("unpackage 1 : " + Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject());
        
        //extend the expration time.
        Date date1 = new Date();
        long t1 = date1.getTime();
        Date expirationTime1 = new Date(t1 + 5000l); //prolongation 5 seconds
        Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().setExpiration(expirationTime1).getSubject();
        
        // check if the extend expiration work.
        Thread.sleep(3000);            
        System.out.println("unpackage 2 : " + Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject());
    } catch (InterruptedException | ExpiredJwtException ex) {
        System.out.println("exception : " + ex.getMessage());
        Thread.currentThread().interrupt();
    }
}

结果是:

紧凑:eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UiLCJleHAiOjE0Mjk2NjU1MjB9.oMY2mDHvNoMZqBfic41LbiKvAyi93wIfu_WgIADb9Wc
解包 0:Joe
解包 1:Joe
异常:JWT在2015-04-22T08:18:40 + 0700过期。当前时间:2015-04-22T08:18:42 + 0700

所以这意味着,unpackage2无法运行,因为它已经过期。

我试图延长过期时间。

因为我将代码应用于Web应用程序。

如果用户仍然连接到我的应用程序,则不应该获取令牌超时。

我找到了一个类似于我的另一个问题


我将你的代码复制粘贴到一个单元测试中,并在JJWT 0.3和0.4上运行,但无法重现你遇到的问题。你能否将你运行代码时的输出粘贴过来? - Erik Gillespie
1个回答

12
问题出在解析代码中:
Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().setExpiration(expirationTime1).getSubject();

在这行代码中,你正在修改解析器返回的JWT。换句话说,上面的代码等同于以下内容:
Jws<Claims> jws = Jwts.parser().setSigningKey(key).parseClaimsJws(compact);
jws.getBody().setExpiration(expirationTime1).getSubject();

注意这段代码如何修改JWT解析器返回的内容?它没有,也无法修改原始的compact字符串表示的JWT。
接下来的代码行尝试解析原始(未修改的)compact字符串。
// check if the extend expiration work.
Thread.sleep(3000);            
System.out.println("unpackage 2 : " + Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject());

但我们知道这行不通,因为更改解析器返回的JWT状态对原始的compact字符串没有任何影响。

如果您的用户向您的Web应用程序提供了一个JWT,并且您想要“延长”令牌的寿命,以便它不会过期,您必须生成一个新的JWT并将该JWT发送回给用户。用户应该在未来的请求中发送新的JWT。您可以重复此过程,只要您想让用户继续与您的Web应用程序交流而无需重新登录。

我应该指出的是,如果您不想担心任何这些事情,Stormpath可以自动为您在浏览器和应用程序之间执行用户和JWT令牌验证 - 您不必自己构建任何内容(披露:我是Stormpath的首席技术官)。

最后,您可能会感兴趣的是,JJWT的测试套件已经验证了正确的行为方式,在许多地方都针对过期和过早使用令牌的情况进行了验证:

但是,您不必相信我的话 :) 这里是您的代码,修改后使您的过期修改按照描述的方式运行:

public class Main {

public static void main(String args[]) {
    byte[] key = new byte[64];
    new SecureRandom().nextBytes(key);
    Date date = new Date();
    long t = date.getTime();
    Date expirationTime = new Date(t + 5000l); // set 5 seconds

    String compact = Jwts.builder().setSubject("Joe").setExpiration(expirationTime).signWith(SignatureAlgorithm.HS256, key).compact();
    System.out.println("compact : " + compact);
    try {
        String unpack = Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject();
        System.out.println("unpackage 0 : " + unpack);

        // check if the expiration work.
        Thread.sleep(3000);
        System.out.println("unpackage 1 : " + Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject());

        //Create a *new* token that reflects a longer extended expiration time.
        Date date1 = new Date();
        long t1 = date1.getTime();
        Date expirationTime1 = new Date(t1 + 5000l); //prolongation 5 seconds

        String compact2 = Jwts.builder().setSubject("Joe").setExpiration(expirationTime1).signWith(SignatureAlgorithm.HS256, key).compact();

        // check if the extend expiration work.
        Thread.sleep(3000);
        System.out.println("unpackage 2 : " + Jwts.parser().setSigningKey(key).parseClaimsJws(compact2).getBody().getSubject());

        Thread.sleep(1000);
    } catch (InterruptedException | ExpiredJwtException ex) {
        System.out.println("exception : " + ex.getMessage());
        Thread.currentThread().interrupt();
    }
}
}

请注意,需要生成第二个新的JWT(“compact2”)以反映新的/最新的到期时间。您不能修改已解析的JWT并期望更改应用于原始紧凑值。
总之,当您需要解析JWT字符串以获取JWT的良好Java对象表示时,请使用Jwts.parser()。当您需要创建或修改JWT以生成新的紧凑字符串表示时,请使用Jwts.builder()。
希望这可以帮助你!

好的,有没有办法只拥有过期的令牌,就能够生成新的令牌?我想要做的是解析过期的令牌,从其主体中获取主题(我知道它已经过期了,但在检查正文和标题是否签名的情况下,它是有效的),然后我想使用这个提取出来的主题来生成有效的身份验证令牌。谢谢。 - hsestupin
好的!我明白了!有一个方法 io.jsonwebtoken.ClaimJwtException#getClaims - hsestupin
1
@hsestupin 不,你绝对不能信任过期的令牌或那些未通过签名验证的令牌。绝对不行。如果你这样做,就明显违反了JWT规范,这是一个黄色警示,需要重新考虑你的设计(你正在尝试做一些应该用其他方式完成的事情)。 - Les Hazlewood
你好,我不理解下面代码的用途: String unpack1 = Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject(); System.out.println("unpackage 0 : " + unpack1);// 检查过期是否有效。 Thread.sleep(3000); String unpack2 = Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject(); System.out.println("unpackage 1 : " +unpack2); - user4821194

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接