Jwt过期时间

这篇具有很好参考价值的文章主要介绍了Jwt过期时间。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

最近在复习Spring Security,发现测试jwt解密的时候会报错,之前没有问题,但是最近几次都出现了问题,我决定究其原因。

Jwt过期时间

debug看一下,

Jwt过期时间

Jwt过期时间

Jwt过期时间

进入真正的解析 

Jwt过期时间

看一下这里的源码

@Override
public Jwt parse(String jwt) throws ExpiredJwtException, MalformedJwtException, SignatureException {

    Assert.hasText(jwt, "JWT String argument cannot be null or empty.");

    String base64UrlEncodedHeader = null;
    String base64UrlEncodedPayload = null;
    String base64UrlEncodedDigest = null;

    int delimiterCount = 0;

    StringBuilder sb = new StringBuilder(128);

    for (char c : jwt.toCharArray()) {

        if (c == SEPARATOR_CHAR) {

            CharSequence tokenSeq = Strings.clean(sb);
            String token = tokenSeq!=null?tokenSeq.toString():null;

            if (delimiterCount == 0) {
                base64UrlEncodedHeader = token;
            } else if (delimiterCount == 1) {
                base64UrlEncodedPayload = token;
            }

            delimiterCount++;
            sb.setLength(0);
        } else {
            sb.append(c);
        }
    }

    if (delimiterCount != 2) {
        String msg = "JWT strings must contain exactly 2 period characters. Found: " + delimiterCount;
        throw new MalformedJwtException(msg);
    }
    if (sb.length() > 0) {
        base64UrlEncodedDigest = sb.toString();
    }

    if (base64UrlEncodedPayload == null) {
        throw new MalformedJwtException("JWT string '" + jwt + "' is missing a body/payload.");
    }

    // =============== Header =================
    Header header = null;

    CompressionCodec compressionCodec = null;

    if (base64UrlEncodedHeader != null) {
        String origValue = TextCodec.BASE64URL.decodeToString(base64UrlEncodedHeader);
        Map<String, Object> m = readValue(origValue);

        if (base64UrlEncodedDigest != null) {
            header = new DefaultJwsHeader(m);
        } else {
            header = new DefaultHeader(m);
        }

        compressionCodec = compressionCodecResolver.resolveCompressionCodec(header);
    }

    // =============== Body =================
    String payload;
    if (compressionCodec != null) {
        byte[] decompressed = compressionCodec.decompress(TextCodec.BASE64URL.decode(base64UrlEncodedPayload));
        payload = new String(decompressed, Strings.UTF_8);
    } else {
        payload = TextCodec.BASE64URL.decodeToString(base64UrlEncodedPayload);
    }

    Claims claims = null;

    if (payload.charAt(0) == '{' && payload.charAt(payload.length() - 1) == '}') { //likely to be json, parse it:
        Map<String, Object> claimsMap = readValue(payload);
        claims = new DefaultClaims(claimsMap);
    }

    // =============== Signature =================
    if (base64UrlEncodedDigest != null) { //it is signed - validate the signature

        JwsHeader jwsHeader = (JwsHeader) header;

        SignatureAlgorithm algorithm = null;

        if (header != null) {
            String alg = jwsHeader.getAlgorithm();
            if (Strings.hasText(alg)) {
                algorithm = SignatureAlgorithm.forName(alg);
            }
        }

        if (algorithm == null || algorithm == SignatureAlgorithm.NONE) {
            //it is plaintext, but it has a signature.  This is invalid:
            String msg = "JWT string has a digest/signature, but the header does not reference a valid signature " +
                         "algorithm.";
            throw new MalformedJwtException(msg);
        }

        if (key != null && keyBytes != null) {
            throw new IllegalStateException("A key object and key bytes cannot both be specified. Choose either.");
        } else if ((key != null || keyBytes != null) && signingKeyResolver != null) {
            String object = key != null ? "a key object" : "key bytes";
            throw new IllegalStateException("A signing key resolver and " + object + " cannot both be specified. Choose either.");
        }

        //digitally signed, let's assert the signature:
        Key key = this.key;

        if (key == null) { //fall back to keyBytes

            byte[] keyBytes = this.keyBytes;

            if (Objects.isEmpty(keyBytes) && signingKeyResolver != null) { //use the signingKeyResolver
                if (claims != null) {
                    key = signingKeyResolver.resolveSigningKey(jwsHeader, claims);
                } else {
                    key = signingKeyResolver.resolveSigningKey(jwsHeader, payload);
                }
            }

            if (!Objects.isEmpty(keyBytes)) {

                Assert.isTrue(algorithm.isHmac(),
                              "Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance.");

                key = new SecretKeySpec(keyBytes, algorithm.getJcaName());
            }
        }

        Assert.notNull(key, "A signing key must be specified if the specified JWT is digitally signed.");

        //re-create the jwt part without the signature.  This is what needs to be signed for verification:
        String jwtWithoutSignature = base64UrlEncodedHeader + SEPARATOR_CHAR + base64UrlEncodedPayload;

        JwtSignatureValidator validator;
        try {
            validator = createSignatureValidator(algorithm, key);
        } catch (IllegalArgumentException e) {
            String algName = algorithm.getValue();
            String msg = "The parsed JWT indicates it was signed with the " +  algName + " signature " +
                         "algorithm, but the specified signing key of type " + key.getClass().getName() +
                         " may not be used to validate " + algName + " signatures.  Because the specified " +
                         "signing key reflects a specific and expected algorithm, and the JWT does not reflect " +
                         "this algorithm, it is likely that the JWT was not expected and therefore should not be " +
                         "trusted.  Another possibility is that the parser was configured with the incorrect " +
                         "signing key, but this cannot be assumed for security reasons.";
            throw new UnsupportedJwtException(msg, e);
        }

        if (!validator.isValid(jwtWithoutSignature, base64UrlEncodedDigest)) {
            String msg = "JWT signature does not match locally computed signature. JWT validity cannot be " +
                         "asserted and should not be trusted.";
            throw new SignatureException(msg);
        }
    }

    final boolean allowSkew = this.allowedClockSkewMillis > 0;

    //since 0.3:
    if (claims != null) {

        SimpleDateFormat sdf;

        final Date now = this.clock.now();
        long nowTime = now.getTime();

        //https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-30#section-4.1.4
        //token MUST NOT be accepted on or after any specified exp time:
        Date exp = claims.getExpiration();
        if (exp != null) {

            long maxTime = nowTime - this.allowedClockSkewMillis;
            Date max = allowSkew ? new Date(maxTime) : now;
            if (max.after(exp)) {
                sdf = new SimpleDateFormat(ISO_8601_FORMAT);
                String expVal = sdf.format(exp);
                String nowVal = sdf.format(now);

                long differenceMillis = maxTime - exp.getTime();

                String msg = "JWT expired at " + expVal + ". Current time: " + nowVal + ", a difference of " +
                    differenceMillis + " milliseconds.  Allowed clock skew: " +
                    this.allowedClockSkewMillis + " milliseconds.";
                throw new ExpiredJwtException(header, claims, msg);
            }
        }

        //https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-30#section-4.1.5
        //token MUST NOT be accepted before any specified nbf time:
        Date nbf = claims.getNotBefore();
        if (nbf != null) {

            long minTime = nowTime + this.allowedClockSkewMillis;
            Date min = allowSkew ? new Date(minTime) : now;
            if (min.before(nbf)) {
                sdf = new SimpleDateFormat(ISO_8601_FORMAT);
                String nbfVal = sdf.format(nbf);
                String nowVal = sdf.format(now);

                long differenceMillis = nbf.getTime() - minTime;

                String msg = "JWT must not be accepted before " + nbfVal + ". Current time: " + nowVal +
                    ", a difference of " +
                    differenceMillis + " milliseconds.  Allowed clock skew: " +
                    this.allowedClockSkewMillis + " milliseconds.";
                throw new PrematureJwtException(header, claims, msg);
            }
        }

        validateExpectedClaims(header, claims);
    }

    Object body = claims != null ? claims : payload;

    if (base64UrlEncodedDigest != null) {
        return new DefaultJws<Object>((JwsHeader) header, body, base64UrlEncodedDigest);
    } else {
        return new DefaultJwt<Object>(header, body);
    }
}

看一下和这次报错相关的代码

final boolean allowSkew = this.allowedClockSkewMillis > 0;

//since 0.3:
if (claims != null) {

    SimpleDateFormat sdf;

    final Date now = this.clock.now();
    long nowTime = now.getTime();

    //https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-30#section-4.1.4
    //token MUST NOT be accepted on or after any specified exp time:
    Date exp = claims.getExpiration();
    if (exp != null) {

        long maxTime = nowTime - this.allowedClockSkewMillis;
        Date max = allowSkew ? new Date(maxTime) : now;
        if (max.after(exp)) {
            sdf = new SimpleDateFormat(ISO_8601_FORMAT);
            String expVal = sdf.format(exp);
            String nowVal = sdf.format(now);

            long differenceMillis = maxTime - exp.getTime();

            String msg = "JWT expired at " + expVal + ". Current time: " + nowVal + ", a difference of " +
                differenceMillis + " milliseconds.  Allowed clock skew: " +
                this.allowedClockSkewMillis + " milliseconds.";
            throw new ExpiredJwtException(header, claims, msg);
        }
    }

    //https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-30#section-4.1.5
    //token MUST NOT be accepted before any specified nbf time:
    Date nbf = claims.getNotBefore();
    if (nbf != null) {

        long minTime = nowTime + this.allowedClockSkewMillis;
        Date min = allowSkew ? new Date(minTime) : now;
        if (min.before(nbf)) {
            sdf = new SimpleDateFormat(ISO_8601_FORMAT);
            String nbfVal = sdf.format(nbf);
            String nowVal = sdf.format(now);

            long differenceMillis = nbf.getTime() - minTime;

            String msg = "JWT must not be accepted before " + nbfVal + ". Current time: " + nowVal +
                ", a difference of " +
                differenceMillis + " milliseconds.  Allowed clock skew: " +
                this.allowedClockSkewMillis + " milliseconds.";
            throw new PrematureJwtException(header, claims, msg);
        }
    }

    validateExpectedClaims(header, claims);
}

final boolean allowSkew = this.allowedClockSkewMillis > 0;

这段代码是 Spring Security 中 JWT 验证过滤器的一部分,用于检查 JWT 的时间戳是否有效。它首先检查 allowedClockSkewMillis 是否大于 0,如果是,则表示允许 JWT 的时间戳与当前时间存在一定的偏差。然后,它通过比较 JWT 中的时间戳(即 iat 和 exp 声明)和当前时间来判断 JWT 是否过期或者尚未生效。如果 JWT 的时间戳在允许的偏差范围内,那么该 JWT 就被认为是有效的。否则,将抛出异常,表示 JWT 已经过期或者尚未生效。

SimpleDateFormat sdf;

final Date now = this.clock.now();
long nowTime = now.getTime();

//https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-30#section-4.1.4
//token MUST NOT be accepted on or after any specified exp time:
Date exp = claims.getExpiration();
if (exp != null) {

    long maxTime = nowTime - this.allowedClockSkewMillis;
    Date max = allowSkew ? new Date(maxTime) : now;
    if (max.after(exp)) {
        sdf = new SimpleDateFormat(ISO_8601_FORMAT);
        String expVal = sdf.format(exp);
        String nowVal = sdf.format(now);

        long differenceMillis = maxTime - exp.getTime();

        String msg = "JWT expired at " + expVal + ". Current time: " + nowVal + ", a difference of " +
            differenceMillis + " milliseconds.  Allowed clock skew: " +
            this.allowedClockSkewMillis + " milliseconds.";
        throw new ExpiredJwtException(header, claims, msg);
    }
}

这段代码是用于检查 JWT 是否已过期的逻辑。首先,它获取当前时间和 JWT 的过期时间,并将它们转换为毫秒数。然后,它比较当前时间与允许的时钟偏差之后的最大时间是否在 JWT 过期时间之前。如果是,则说明 JWT 仍然有效;否则,就抛出一个 ExpiredJwtException 异常,表示 JWT 已过期。

在比较时间之前,代码还考虑了允许的时钟偏差。这是因为即使是在同一台计算机上,不同的程序可能会使用不同的时钟,导致它们之间存在一定的时间差。为了解决这个问题,代码允许在当前时间基础上加上一定的时钟偏差,从而容忍这种时间差。

最后,如果 JWT 已过期,代码会生成一条错误消息,其中包括 JWT 的过期时间、当前时间以及它们之间的时间差。这条错误消息将被传递给 ExpiredJwtException 异常,以便调用方可以捕获并处理它。

Date exp = claims.getExpiration();

这段代码是用来获取 JWT 的过期时间的。在 JWT 中,通常会包含一个 exp 字段,用来表示 JWT 的过期时间。在使用 JWT 进行认证时,需要检查当前时间是否在 JWT 的有效期内,如果超过了有效期,则认证失败。

首先,通过 claims.getExpiration() 方法获取 JWT 中的过期时间。claims 是一个包含 JWT 所有声明信息的对象,可以通过它来获取 JWT 中的各种信息。getExpiration() 方法就是用来获取 JWT 的过期时间的。

如果 JWT 中没有设置过期时间,或者过期时间无效(比如格式不正确),则 getExpiration() 方法会返回 null。因此,在使用 JWT 进行认证时,需要先判断 exp 是否为 null,以及当前时间是否在有效期内,才能确定 JWT 是否有效。

long maxTime = nowTime - this.allowedClockSkewMillis;
Date max = allowSkew ? new Date(maxTime) : now;

这段代码是用来计算 JWT 的最大有效期的。JWT 中包含了一个时间戳,以便在验证时检查 JWT 是否已经过期。但是由于客户端和服务器之间的时间可能存在差异,因此需要考虑一些时钟偏差。这里的 allowedClockSkewMillis 变量就是用来设置时钟偏差的毫秒数。

首先,获取当前时间戳 nowTime,然后减去允许的时钟偏差 allowedClockSkewMillis 得到最大有效期的时间戳 maxTime。接着,根据是否允许时钟偏差来创建一个 Date 对象 max,如果允许时钟偏差,则使用 maxTime 创建 Date 对象;否则,使用当前时间 now 创建 Date 对象。

最终,max 就是 JWT 的最大有效期,用于在验证 JWT 时检查时间戳是否在有效期内。

Jwt过期时间

显然,不允许允许时钟偏差

 判断最大有效期已经超过过期时间,返回true,说明Jwt已经过期

Jwt过期时间

Jwt过期时间

直接放行

Jwt过期时间

public static final Long JWT_TTL = 60 * 60 * 1000L;// 60 * 60 *1000  一个小时

我设置了过期时间,之前只是使用,但是没有仔细看(只怪我学的粗心,只是用了,没有仔细看)

这是jwt工具类

/**
 * JWT工具类
 */
public class JwtUtil {

    //有效期为
    public static final Long JWT_TTL = 60 * 60 * 1000L;// 60 * 60 *1000  一个小时
    //设置秘钥明文
    public static final String JWT_KEY = "sPowerveil";

    public static String getUUID() {
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        return token;
    }

    /**
     * 生成jtw
     *
     * @param subject token中要存放的数据(json格式)
     * @return
     */
    public static String createJWT(String subject) {
        JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间
        return builder.compact();
    }

    /**
     * 生成jtw
     *
     * @param subject   token中要存放的数据(json格式)
     * @param ttlMillis token超时时间
     * @return
     */
    public static String createJWT(String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间
        return builder.compact();
    }

    private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        SecretKey secretKey = generalKey();
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        if (ttlMillis == null) {
            ttlMillis = JwtUtil.JWT_TTL;
        }
        long expMillis = nowMillis + ttlMillis;
        Date expDate = new Date(expMillis);
        return Jwts.builder()
                .setId(uuid)              //唯一的ID
                .setSubject(subject)   // 主题  可以是JSON数据
                .setIssuer("pv")     // 签发者
                .setIssuedAt(now)      // 签发时间
                .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
                .setExpiration(expDate);
    }

    /**
     * 创建token
     *
     * @param id
     * @param subject
     * @param ttlMillis
     * @return
     */
    public static String createJWT(String id, String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间
        return builder.compact();
    }

    public static void main(String[] args) throws Exception {
        System.out.println(JWT_KEY.length());
        System.out.println("==============================================");
        String jwt = createJWT("123456");
        System.out.println(jwt);

    }

    /**
     * 生成加密后的秘钥 secretKey
     *
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    /**
     * 解析
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) throws Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();
    }
}

最后感谢OpenAI提供帮助,辅助我解决问题。

Jwt过期时间

 文章来源地址https://www.toymoban.com/news/detail-483718.html

到了这里,关于Jwt过期时间的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Security 构建基于 JWT 的登录认证

    一言以蔽之,JWT 可以携带非敏感信息,并具有不可篡改性。可以通过验证是否被篡改,以及读取信息内容,完成网络认证的三个问题:“你是谁”、“你有哪些权限”、“是不是冒充的”。   为了安全,使用它需要采用 Https 协议,并且一定要小心防止用于加密的密钥泄露。

    2024年02月16日
    浏览(32)
  • Spring Security详细讲解(JWT+SpringSecurity登入案例)

    1.SpringSecurity SpringSecurity 是一个功能强大且高度可定制的身份验证和访问控制框架 。它是保护基于 Spring 的应用程序的事实上的标准。 SpringSecurity 是一个致力于为 Java 应用程序提供身份验证和授权的框架 。像所有 Spring 项目一样,Spring Security 的真正强大之处在于它可以如何轻

    2024年02月02日
    浏览(35)
  • spring-security -oauth2 整合 JWT

    在这个基础上,进行整合。 spring security oauth2学习 -- 快速入门_本郡主是喵的博客-CSDN博客 先把  reids,common-pools  等依赖删掉。 删掉redis的下相关配置 1.1 导入依赖 1.2 核心代码 创建 jwtTokenConfig.java 在 AuthenticationServer.java 里面新增这些。  运行,启动!  复制这个token去官网解析

    2024年02月09日
    浏览(44)
  • Java开发 - 单点登录初体验(Spring Security + JWT)

    目录 ​​​​​​​ 前言 为什么要登录 登录的种类 Cookie-Session Cookie-Session-local storage JWT令牌 几种登陆总结  用户身份认证与授权 创建工程 添加依赖 启动项目 Bcrypt算法的工具 创建VO模型类 创建接口文件 创建XML文件 补充配置 添加依赖 添加配置 创建配置类 测试上面的配置

    2024年02月02日
    浏览(36)
  • 【Spring Security】使用 OncePerRequestFilter 过滤器校验登录过期、请求日志等操作

    OncePerRequestFilter 是一个过滤器,每个请求都会执行一次;一般开发中主要是做检查是否已登录、Token是否过期和授权等操作,而每个操作都是一个过滤器,下面演示一下。 检查是否登录过期过滤器 检查是否登录过期过滤器 End

    2024年02月10日
    浏览(54)
  • SpringBoot2.3集成Spring Security(二) JWT认证

    紧接上文,我们已经完成了 SpringBoot中集成Spring Security,并且用户名帐号和密码都是从数据库中获取。但是这种方式还是不能满足现在的开发需求。 使用JWT的好处: 无状态认证:JWT本身包含了认证信息和声明,服务器不需要在会话中保存任何状态。这样使得应用程序可以更加

    2024年02月11日
    浏览(45)
  • Spring Security OAuth 2.0 资源服务器— JWT

    目录 一、JWT的最小依赖 二、JWT的最基本配置 1、指定授权服务器 2、初始预期(Startup Expectations) 3、运行时预期(Runtime Expectations) 三、JWT认证是如何工作的 四、直接指定授权服务器 JWK Set Uri 五、提供 audiences 六、覆盖或取代启动自动配置 1、使用jwkSetUri() 2、使用decoder()

    2024年02月05日
    浏览(45)
  • Spring Boot 优雅集成 Spring Security 5.7(安全框架)与 JWT(双令牌机制)

    本章节将介绍 Spring Boot 集成 Spring Security 5.7(安全框架)。 🤖 Spring Boot 2.x 实践案例(代码仓库) Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。 它提供了一组可以在 Spring 应用上下文中配置的 Bean,充分利用了 Spring

    2024年02月12日
    浏览(34)
  • SpringCloud gateway+Spring Security + JWT实现登录和用户权限校验

    原本打算将Security模块与gateway模块分开写的,但想到gateway本来就有过滤的作用 ,于是就把gateway和Security结合在一起了,然后结合JWT令牌对用户身份和权限进行校验。 Spring Cloud的网关与传统的SpringMVC不同,gateway是基于Netty容器,采用的webflux技术,所以gateway模块不能引入spri

    2024年02月03日
    浏览(39)
  • 【深入浅出 Spring Security(十三)】使用 JWT 进行前后端分离认证(附源码)

    JWT 全称 Java web Token,在此所讲述的是 JWT 用于身份认证,用服务器端生成的JWT去替代原始的Session认证,以提高安全性。 JWT本质是一个Token令牌,是由三部分组成的字符串,分别是头部(header)、载荷(payload)和签名(signature)。头部一般包含该 JWT 的基本信息,例如所使用的

    2024年02月12日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包