Jwt简介+工具类应用+Jwt集成spa项目

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Jwt简介+工具类应用+Jwt集成spa项目

一、Jwt简介

1.1 Jwt是什么

JWT,全称 JSON Web Token,是一种紧凑的、自包含的标准,用于在不同系统之间安全地传输信息。JWT 是一个开放标准(RFC 7519),它定义了一种简洁而独立的方法,用于在各方之间作为 JSON 对象安全地传输信息。通常,JWT 用于身份验证和授权,尤其在网络应用程序和服务之间。

1.2 为什么使用Jwt

使用JWT(JSON Web Token)有许多优点,其中包括以下几个主要原因:

  1. 轻量且自包含:JWT 是一种紧凑的数据格式,易于传输和处理。它包含了所有必要的信息,不需要在服务器端保留会话状态,因为所有的信息都存储在令牌中。这使得它特别适合用于分布式系统和跨域应用之间的通信。
  2. 无状态性:JWT 是无状态的,服务器不需要在处理请求时保留任何会话数据。这降低了服务器的负担,并有助于构建可伸缩的应用程序。
  3. 安全性:JWT 可以使用密钥进行签名,以验证令牌的完整性。只有拥有正确密钥的人才能生成有效的签名。这有助于防止令牌被篡改。此外,JWT 还可以进行加密,以保护负载中的敏感数据。
  4. 广泛支持:JWT 是一个开放标准,因此在不同的编程语言和平台上都有广泛的支持。这意味着您可以在各种技术栈中使用JWT,并且不受特定编程语言的限制。
  5. 适用于身份验证和授权:JWT 可以用于用户身份验证,允许用户在不再次提供凭证的情况下访问受保护的资源。它还可以包含用户的权限信息,用于授权访问。这使得JWT非常适合构建单点登录(SSO)系统和授权解决方案。
  6. 跨域应用支持:JWT 可以轻松用于跨不同域、不同应用程序之间的身份验证和数据传输。它允许不同系统之间共享用户身份和权限信息,而不需要复杂的集成或会话共享机制。
  7. 可自定义:JWT 的负载部分是自定义的,您可以在其中包含所需的声明信息。这使得JWT非常灵活,可以满足各种应用程序的需求。

尽管JWT具有这些优点,但也需要小心使用。密钥管理和令牌的保护非常关键,因为泄露密钥或令牌可能导致安全风险。此外,JWT 不适合存储大量敏感信息,因为它是基于 Base64 编码的,不具备加密级别的安全性。在设计和实施JWT时,需要综合考虑这些因素。

1.3 Jwt的工作原理

JWT(JSON Web Token)是一种用于在网络应用之间安全传输信息的开放标准(RFC 7519)。它工作的原理可以分为三个主要步骤:创建令牌、发送令牌、验证令牌。

1.创建令牌(Token Creation):

  • Header(头部):JWT以Base64编码的JSON对象开始,其中包含了描述JWT的元数据,例如算法(HMAC SHA256或RSA等)。
  • Payload(负载):接下来是包含声明的有效负载。声明是有关实体(通常是用户)以及关于令牌本身的信息。有效负载包括标准声明(例如,发行人、受众、过期时间)和自定义声明。有效负载也以Base64编码的JSON对象形式存在。
  • Signature(签名):为了保证令牌的完整性,有效负载和头部通常会使用密钥进行签名。签名的过程会根据头部中指定的算法进行计算。签名部分是使用算法和密钥将Base64编码的头部和有效负载连接在一起生成的签名。
  • 最终,JWT 由头部、有效负载和签名三个部分组成,它们以点号分隔构成一个字符串,如下所示:header.payload.signature

2.发送令牌(Token Transmission)

  • 一旦JWT被创建,它可以通过HTTP请求的标头(通常是Authorization标头)或作为参数附加到URL中进行传输。JWT通常作为Bearer令牌发送,这意味着请求方需要在HTTP标头中包含一个令牌,格式为Bearer <token>

3.验证令牌(Token Verification):

  • 接收方(通常是服务器)接收JWT后,首先需要解析JWT,通常通过分割字符串并解码Base64编码的头部和有效负载来完成。
  • 接收方验证签名,确保令牌在传输过程中没有被篡改。这是通过使用在令牌创建时使用的密钥来计算新的签名并将其与接收到的签名进行比较来完成的。
  • 接收方检查有效负载中的声明,例如过期时间,以确保令牌仍然有效。
  • 如果一切正常,接收方信任令牌,允许用户访问所请求的资源或执行相应的操作。

总之,JWT的工作原理基于创建、发送和验证令牌。它允许在不需要维护会话状态的情况下,安全地传输信息,并在各种应用程序和服务之间实现身份验证和授权。密钥的安全管理对JWT的安全性至关重要,因为泄露密钥可能导致令牌被篡改。

1.4 Jwt的组成

JSON Web Tokens (JWT)时,JWT通常由三个部分组成,它们使用点号(.)分隔开。这三个部分是:

  1. Header(头部):包含了令牌的元数据,通常包括令牌类型和所使用的签名算法。
  2. Payload(载荷):包含了声明,用于在令牌中包含有关实体(例如用户)和其他数据的信息。声明分为三种类型:注册声明、公共声明和私有声明。
  3. Signature(签名):用于验证令牌的完整性和真实性,以确保令牌在传输过程中没有被篡改。

这三个部分组合在一起构成了JWT,它通常用于身份验证和授权等场景。

1.5 Jwt的验证过程

JWT(JSON Web Token)的验证过程通常涉及以下步骤:

  1. 解析JWT:首先,你需要将JWT令牌分解成其三个部分:头部(Header)、载荷(Payload)和签名(Signature)。这通常涉及将JWT令牌中的Base64编码部分解码,并分隔它们以获取明文数据。
  2. 验证头部:在验证JWT时,你应该首先检查头部部分,确保它包含正确的算法(通常是HMAC SHA256或RSA等)。此步骤用于验证签名的算法是否与你的预期一致。
  3. 获取密钥:根据验证头部的结果,你需要获取用于验证JWT签名的密钥。这个密钥可以是对称密钥(用于HMAC算法)或公钥(用于RSA算法)。密钥通常是预先共享的或从信任的身份提供者处获取的。
  4. 验证签名:使用得到的密钥对JWT令牌中的签名部分进行验证。如果你在头部指定了HMAC SHA256算法,那么你需要使用相同的密钥对载荷和头部的内容进行签名,并将结果与JWT令牌中的签名部分进行比较。如果它们匹配,说明JWT是有效的。
  5. 验证有效期:检查JWT令牌中的"exp"(过期时间)声明,确保令牌尚未过期。如果令牌已过期,它将被视为无效。
  6. 验证其他声明:根据你的需求,你可以验证其他JWT声明,例如"iss"(发行者)、"aud"(受众)、"sub"(主题)等。这些验证步骤可以根据你的具体需求进行定制。
  7. 成功验证:如果JWT通过了所有的验证步骤,那么它被认为是有效的,你可以信任其内容,例如载荷中的用户标识信息。

请注意,JWT的验证过程可以根据使用情况和需求而有所不同。你可以选择是否要验证特定声明、如何处理过期令牌等。验证过程的安全性和严格性取决于你的具体应用场景和安全要求。

1.6 JWT令牌刷新思路

JWT(JSON Web Token)令牌的刷新通常涉及以下思路:

  1. 令牌过期时间(exp): 在JWT中,可以设置一个过期时间(exp)声明,指示令牌的有效期。当令牌过期时,就需要进行刷新。
  2. 刷新令牌(Refresh Token): 除了JWT本身外,可以使用刷新令牌来实现令牌的刷新机制。刷新令牌是一个长期有效的令牌,用于获取新的JWT。刷新令牌通常具有自己的过期时间,较长,用于延长用户的会话。
  3. 刷新请求: 当JWT过期或即将过期时,客户端可以使用刷新令牌向身份验证服务器发送刷新请求。该请求通常包含刷新令牌和其他必要的信息。
  4. 身份验证服务器处理: 身份验证服务器接收到刷新请求后,会验证刷新令牌的有效性。如果刷新令牌有效,身份验证服务器会生成新的JWT,并将其返回给客户端。
  5. 更新本地存储: 客户端收到新的JWT后,可以更新本地存储中的令牌,以便在将来的请求中使用。
  6. 刷新令牌的限制: 要确保刷新令牌的安全性,应该采取一些措施,如定期更换刷新令牌、限制刷新令牌的使用次数等,以减小滥用的风险。

示例流程:

  • 初始登录时,用户获得JWT和刷新令牌。
  • 在JWT即将过期时,客户端使用刷新令牌向身份验证服务器请求新的JWT。
  • 身份验证服务器验证刷新令牌,如果有效,则生成新的JWT并返回给客户端。
  • 客户端使用新的JWT更新本地存储的令牌。
  • 重复上述过程,使用户的身份保持有效。

需要注意的是,刷新令牌的使用需要谨慎,要确保它的安全性,避免在不安全的环境中暴露。此外,刷新令牌的管理和使用涉及到具体的安全策略和实现细节,需要根据具体的应用场景进行调整。

二、Jwt工具类

2.1 Jwt工具类是什么

在 Java 中,你可以使用第三方库来处理 JWT(JSON Web Tokens)。其中,"Java JWT" (jjwt) 是一个常用的库,用于创建、解析和验证 JWT 令牌。

2.2 Jwt工具类的使用

JwtUtils:

package com.zking.ssm.jwt;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
/**
 * JWT验证过滤器:配置顺序 CorsFilte->JwtUtilsr-->StrutsPrepareAndExecuteFilter
 *
 */
public class JwtUtils {
  /**
   * JWT_WEB_TTL:WEBAPP应用中token的有效时间,默认30分钟
   */
  public static final long JWT_WEB_TTL = 30 * 60 * 1000;
  /**
   * 将jwt令牌保存到header中的key
   */
  public static final String JWT_HEADER_KEY = "jwt";
  // 指定签名的时候使用的签名算法,也就是header那部分,jwt已经将这部分内容封装好了。
  private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
  private static final String JWT_SECRET = "f356cdce935c42328ad2001d7e9552a3";// JWT密匙
  private static final SecretKey JWT_KEY;// 使用JWT密匙生成的加密key
  static {
    byte[] encodedKey = Base64.decodeBase64(JWT_SECRET);
    JWT_KEY = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
  }
  private JwtUtils() {
  }
  /**
   * 解密jwt,获得所有声明(包括标准和私有声明)
   * 
   * @param jwt
   * @return
   * @throws Exception
   */
  public static Claims parseJwt(String jwt) {
    Claims claims = Jwts.parser()
        .setSigningKey(JWT_KEY)
        .parseClaimsJws(jwt)
        .getBody();
    return claims;
  }
  /**
   * 创建JWT令牌,签发时间为当前时间
   * 
   * @param claims
   *            创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
   * @param ttlMillis
   *            JWT的有效时间(单位毫秒),当前时间+有效时间=过期时间
   * @return jwt令牌
   */
  public static String createJwt(Map<String, Object> claims, 
      long ttlMillis) {
    // 生成JWT的时间,即签发时间 2021-10-30 10:02:00 -> 30 10:32:00
    long nowMillis = System.currentTimeMillis();
    //链式语法:
    // 下面就是在为payload添加各种标准声明和私有声明了
    // 这里其实就是new一个JwtBuilder,设置jwt的body
    JwtBuilder builder = Jwts.builder()
        // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
        .setClaims(claims)
        // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
        // 可以在未登陆前作为身份标识使用
        .setId(UUID.randomUUID().toString().replace("-", ""))
        // iss(Issuser)签发者,写死
        .setIssuer("zking")
        // iat: jwt的签发时间
        .setIssuedAt(new Date(nowMillis))
        // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
        // .setSubject("{}")
        // 设置签名使用的签名算法和签名使用的秘钥
        .signWith(SIGNATURE_ALGORITHM, JWT_KEY)
        // 设置JWT的过期时间
        .setExpiration(new Date(nowMillis + ttlMillis));
    return builder.compact();
  }
  /**
   * 复制jwt,并重新设置签发时间(为当前时间)和失效时间
   * 
   * @param jwt
   *            被复制的jwt令牌
   * @param ttlMillis
   *            jwt的有效时间(单位毫秒),当前时间+有效时间=过期时间
   * @return
   */
  public static String copyJwt(String jwt, Long ttlMillis) {
    //解密JWT,获取所有的声明(私有和标准)
    //old
    Claims claims = parseJwt(jwt);
    // 生成JWT的时间,即签发时间
    long nowMillis = System.currentTimeMillis();
    // 下面就是在为payload添加各种标准声明和私有声明了
    // 这里其实就是new一个JwtBuilder,设置jwt的body
    JwtBuilder builder = Jwts.builder()
        // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
        .setClaims(claims)
        // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
        // 可以在未登陆前作为身份标识使用
        //.setId(UUID.randomUUID().toString().replace("-", ""))
        // iss(Issuser)签发者,写死
        // .setIssuer("zking")
        // iat: jwt的签发时间
        .setIssuedAt(new Date(nowMillis))
        // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
        // .setSubject("{}")
        // 设置签名使用的签名算法和签名使用的秘钥
        .signWith(SIGNATURE_ALGORITHM, JWT_KEY)
        // 设置JWT的过期时间
        .setExpiration(new Date(nowMillis + ttlMillis));
    return builder.compact();
  }
}

2.2.1 生成Jwt

@Test
  public void test1() {// 生成JWT
    //JWT Token=Header.Payload.Signature
    //头部.载荷.签名
    //Payload=标准声明+私有声明+公有声明
    //定义私有声明
    Map<String, Object> claims = new HashMap<String, Object>();
    claims.put("username", "zss");
    claims.put("age", 18);
      //TTL:Time To Live
    String jwt = JwtUtils.createJwt(claims, JwtUtils.JWT_WEB_TTL);
    System.out.println(jwt);
    //获取Payload(包含标准和私有声明)
    Claims parseJwt = JwtUtils.parseJwt(jwt);
    for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {
      System.out.println(entry.getKey() + "=" + entry.getValue());
    }
    Date d1 = parseJwt.getIssuedAt();
    Date d2 = parseJwt.getExpiration();
    System.out.println("令牌签发时间:" + sdf.format(d1));
    System.out.println("令牌过期时间:" + sdf.format(d2));
  }

用于生成JWT。在该方法中,定义了私有声明(claims),并设置了一些键值对,例如用户名和年龄。然后调用了JwtUtils.createJwt()方法生成JWT,并打印输出。

复制此段生成的jwt,方便下一个方法进行解析如下:

2.2.2 解析Jwt

@Test
  public void test2() {// 解析oldJwt
    //io.jsonwebtoken.ExpiredJwtException:JWT过期异常
    //io.jsonwebtoken.SignatureException:签名异常
    //String oldJwt="eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTA3MTg2NzcsImlhdCI6MTU5MDcxNjg3NywiYWdlIjoxOCwianRpIjoiNDFmZjFiZGFkYzkxNDA3OGE4ZGUyNGRkZDEwYjU4N2IiLCJ1c2VybmFtZSI6InpzcyJ9.DdPvioX6kuhV6lEfD9QAN2eQSk_mO3dYkmDmTQsqa78";
    //eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzU1NjE3MjcsImlhdCI6MTYzNTU1OTkyNywiYWdlIjoxOCwianRpIjoiN2RlYmIzM2JiZTg3NDBmODgzNDI5Njk0ZWE4NzcyMTgiLCJ1c2VybmFtZSI6InpzcyJ9.dUR-9JUlyRdoYx-506SxXQ3gbHFCv0g5Zm8ZGzK1fzw
    String newJwt="eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ6a2luZyIsImV4cCI6MTY2MjM0Njg3MSwiaWF0IjoxNjYyMzQ1MDcxLCJhZ2UiOjE4LCJqdGkiOiI4YjllNzc3YzFlMDM0MjViYThmMDVjNTFlMTU3NDQ1MiIsInVzZXJuYW1lIjoienNzIn0.UWpJxPxwJ09PKxE2SY5ME41W1Kv3jP5bZGKK-oNUDuM";
    String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzU1NjE3MjcsImlhdCI6MTYzNTU1OTkyNywiYWdlIjoxOCwianRpIjoiN2RlYmIzM2JiZTg3NDBmODgzNDI5Njk0ZWE4NzcyMTgiLCJ1c2VybmFtZSI6InpzcyJ9.dUR-9JUlyRdoYx-506SxXQ3gbHFCv0g5Zm8ZGzK1fzw";
    Claims parseJwt = JwtUtils.parseJwt(newJwt);
    for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {
      System.out.println(entry.getKey() + "=" + entry.getValue());
    }
    Date d1 = parseJwt.getIssuedAt();
    Date d2 = parseJwt.getExpiration();
    System.out.println("令牌签发时间:" + sdf.format(d1));
    System.out.println("令牌过期时间:" + sdf.format(d2));
  }

此处放的是很久之前的一段登录的Jwt会出现Jwt过期异常,如下:

然后我们把刚刚复制的Jwt串放入代码指定地方重新进行解析试试,优化后的代码如下:

@Test
  public void test2() {// 解析oldJwt
    //io.jsonwebtoken.ExpiredJwtException:JWT过期异常
    //io.jsonwebtoken.SignatureException:签名异常
    //String oldJwt="eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTA3MTg2NzcsImlhdCI6MTU5MDcxNjg3NywiYWdlIjoxOCwianRpIjoiNDFmZjFiZGFkYzkxNDA3OGE4ZGUyNGRkZDEwYjU4N2IiLCJ1c2VybmFtZSI6InpzcyJ9.DdPvioX6kuhV6lEfD9QAN2eQSk_mO3dYkmDmTQsqa78";
    //eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzU1NjE3MjcsImlhdCI6MTYzNTU1OTkyNywiYWdlIjoxOCwianRpIjoiN2RlYmIzM2JiZTg3NDBmODgzNDI5Njk0ZWE4NzcyMTgiLCJ1c2VybmFtZSI6InpzcyJ9.dUR-9JUlyRdoYx-506SxXQ3gbHFCv0g5Zm8ZGzK1fzw
    String newJwt="eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ6a2luZyIsImV4cCI6MTY5NzE4OTgxOSwiaWF0IjoxNjk3MTg4MDE5LCJhZ2UiOjE4LCJqdGkiOiJmOWM2MWE0YTRiZGE0OTY3YmY0NGM2ZjI3MmNiNzg2MSIsInVzZXJuYW1lIjoienNzIn0.AeIWdiKlOugiSB11PoA1s75u9kAq72RgX_idV-IkG_w";
    String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzU1NjE3MjcsImlhdCI6MTYzNTU1OTkyNywiYWdlIjoxOCwianRpIjoiN2RlYmIzM2JiZTg3NDBmODgzNDI5Njk0ZWE4NzcyMTgiLCJ1c2VybmFtZSI6InpzcyJ9.dUR-9JUlyRdoYx-506SxXQ3gbHFCv0g5Zm8ZGzK1fzw";
    Claims parseJwt = JwtUtils.parseJwt(newJwt);
    for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {
      System.out.println(entry.getKey() + "=" + entry.getValue());
    }
    Date d1 = parseJwt.getIssuedAt();
    Date d2 = parseJwt.getExpiration();
    System.out.println("令牌签发时间:" + sdf.format(d1));
    System.out.println("令牌过期时间:" + sdf.format(d2));
  }

然后重新运行Test2方法,结果如下:

2.2.3 复制JWT并延时30分钟

@Test
  public void test3() {// 复制jwt,并延时30分钟
    String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ6a2luZyIsImV4cCI6MTY5NzE4OTgxOSwiaWF0IjoxNjk3MTg4MDE5LCJhZ2UiOjE4LCJqdGkiOiJmOWM2MWE0YTRiZGE0OTY3YmY0NGM2ZjI3MmNiNzg2MSIsInVzZXJuYW1lIjoienNzIn0.AeIWdiKlOugiSB11PoA1s75u9kAq72RgX_idV-IkG_w";
    //String newJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDU3NTM2NTUsImlhdCI6MTYwNTc1MTg1NSwiYWdlIjoxOCwianRpIjoiYmNmN2Q1MzQ2YjE3NGU2MDk1MmIxYzQ3ZTlmMzQyZjgiLCJ1c2VybmFtZSI6InpzcyJ9.m1Qn84RxgbKCnsvrdbbAnj8l_5Jwovry8En0j4kCxhc";
    //String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI5MDMzNjAsImlhdCI6MTU2MjkwMTU2MCwiYWdlIjoxOCwianRpIjoiZDVjMzE4Njg0MDcyNDgyZDg1MDE5ODVmMDY3OGQ4NjkiLCJ1c2VybmFtZSI6InpzcyJ9.XDDDRRq5jYq5EdEBHtPm7GcuBz4S0VhDTS1amRCdf48";
    String newJwt = JwtUtils.copyJwt(oldJwt, JwtUtils.JWT_WEB_TTL);
    System.out.println(newJwt);
    Claims parseJwt = JwtUtils.parseJwt(newJwt);
    for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {
      System.out.println(entry.getKey() + "=" + entry.getValue());
    }
    Date d1 = parseJwt.getIssuedAt();
    Date d2 = parseJwt.getExpiration();
    System.out.println("令牌签发时间:" + sdf.format(d1));
    System.out.println("令牌过期时间:" + sdf.format(d2));
  }

2.2.4 测试JWT的有效时间

@Test
  public void test4() {// 测试JWT的有效时间
    Map<String, Object> claims = new HashMap<String, Object>();
    claims.put("username", "zss");
    String jwt = JwtUtils.createJwt(claims, 3 * 1000L);
    System.out.println(jwt);
    Claims parseJwt = JwtUtils.parseJwt(jwt);
    Date d1 = parseJwt.getIssuedAt();
    Date d2 = parseJwt.getExpiration();
    System.out.println("令牌签发时间:" + sdf.format(d1));
    System.out.println("令牌过期时间:" + sdf.format(d2));
  }

2.2.5 模拟过期JWT的解析

@Test
  public void test5() {// 三秒后再解析上面过期时间只有三秒的令牌,因为过期则会报错io.jsonwebtoken.ExpiredJwtException
    //String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzU1NjMzODIsImlhdCI6MTYzNTU2MTU4MiwiYWdlIjoxOCwianRpIjoiN2RlYmIzM2JiZTg3NDBmODgzNDI5Njk0ZWE4NzcyMTgiLCJ1c2VybmFtZSI6InpzcyJ1.F4pZFCjWP6wlq8v_udfhOkNCpErF5QlL7DXJdzXTHqE";
    String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ6a2luZyIsImV4cCI6MTY5NzE3MzYwMywiaWF0IjoxNjk3MTczNjAwLCJqdGkiOiIwNWZiYzM1Nzc0MWU0ZGY4YTAyOTU0YTlmMGQ0N2I3NyIsInVzZXJuYW1lIjoienNzIn0.1hxptBpX9w_CfVKhJ19KDWoKzhtmZwbMWpnJkoGKzcE";
    Claims parseJwt = JwtUtils.parseJwt(oldJwt);
    // 过期后解析就报错了,下面代码根本不会执行
    Date d1 = parseJwt.getIssuedAt();
    Date d2 = parseJwt.getExpiration();
    System.out.println("令牌签发时间:" + sdf.format(d1));
    System.out.println("令牌过期时间:" + sdf.format(d2));
  }

三、Jwt集成进spa项目

思路:

1.user登录方法,要放开用户信息生成jwt串保存到响应头中的代码

2.关闭jwtfilter中的off开关,代表开启jwt验证

3.crosfilter中要允许jwt使用请求头及响应头,换句话说web.xml要更换配置

具体操作如下:

web.xml:

<!--CrosFilter跨域过滤器-->
  <filter>
    <filter-name>corsFilter</filter-name>
    <filter-class>com.zking.ssm.util.CorsFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>corsFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

然后将前端代码优化,如下:

state.js:

export default{
  eduName:'死亡60HZ的支配',
  aname:'',
  bname:'',
  jwt:''
}

mutation.js:

export default {
  setEduName: (state, payload) => {
    //state指的就是state.js文件中导出的对象
    //payload就是vue文件传递过来的参数
    // A.Vue name  {a_name:this.name}
    // B.Vue name  {b_name:this.name}
    // state.aname = payload.a_name;
    // state.bname = payload.b_name;
    state.eduName = payload.eduName;
  },
  setJwt: (state, payload) => {
    state.jwt = payload.jwt;
  }
}

gettter.js:

export default {
  getEduName: (state) => {
    return state.eduname;
  },
  getJwt: (state) => {
    return state.jwt;
  }
}

最后在Api目录下找到http.js,把超时时间改成30s,如下:

// axios默认配置
axios.defaults.timeout = 30000; // 超时时间

在请求跟相应拦截器的代码中加入判断,如下:

// 请求拦截器
axios.interceptors.request.use(function(config) {
  let jwt =window.vm.$store.getters.getJwt;
  if(jwt){
  config.headers['jwt'] = jwt;
  }
  return config;
}, function(error) {
  return Promise.reject(error);
});
// 响应拦截器
axios.interceptors.response.use(function(response) {
  let jwt = response.headers['jwt'];
  if(jwt){
    //要将响应头中的jwt串放入到state.js中
    window.vm.$store.commit('setJwt',{
      jwt:jwt
    });
  }
  return response;
}, function(error) {
  return Promise.reject(error);
});

完整页面如下,http.js:

/**
 * vue项目对axios的全局配置
 */
import axios from 'axios'
import qs from 'qs'
import Vue from 'vue';
//引入action模块,并添加至axios的类属性urls上
import action from '@/api/action'
// import { compile } from 'vue/types/umd';
axios.urls = action
// axios默认配置
axios.defaults.timeout = 30000; // 超时时间
// axios.defaults.baseURL = 'http://localhost:8080/j2ee15'; // 默认地址
axios.defaults.baseURL = action.SERVER;
//整理数据
// 只适用于 POST,PUT,PATCH,transformRequest` 允许在向服务器发送前,修改请求数据
axios.defaults.transformRequest = function(data) {
  data = qs.stringify(data);
  return data;
};
// 请求拦截器
axios.interceptors.request.use(function(config) {
  let jwt =window.vm.$store.getters.getJwt;
  if(jwt){
  config.headers['jwt'] = jwt;
  }
  return config;
}, function(error) {
  return Promise.reject(error);
});
// 响应拦截器
axios.interceptors.response.use(function(response) {
  let jwt = response.headers['jwt'];
  if(jwt){
    //要将响应头中的jwt串放入到state.js中
    window.vm.$store.commit('setJwt',{
      jwt:jwt
    });
  }
  return response;
}, function(error) {
  return Promise.reject(error);
});
// // 路由请求拦截
// // http request 拦截器
// axios.interceptors.request.use(
//  config => {
//    //config.data = JSON.stringify(config.data);
//    //config.headers['Content-Type'] = 'application/json;charset=UTF-8';
//    //config.headers['Token'] = 'abcxyz';
//    //判断是否存在ticket,如果存在的话,则每个http header都加上ticket
//    // if (cookie.get("token")) {
//    //  //用户每次操作,都将cookie设置成2小时
//    //  cookie.set("token", cookie.get("token"), 1 / 12)
//    //  cookie.set("name", cookie.get("name"), 1 / 12)
//    //  config.headers.token = cookie.get("token");
//    //  config.headers.name = cookie.get("name");
//    // }
//    return config;
//  },
//  error => {
//    return Promise.reject(error.response);
//  });
// // 路由响应拦截
// // http response 拦截器
// axios.interceptors.response.use(
//  response => {
//    if (response.data.resultCode == "404") {
//      console.log("response.data.resultCode是404")
//      // 返回 错误代码-1 清除ticket信息并跳转到登录页面
//      //      cookie.del("ticket")
//      //      window.location.href='http://login.com'
//      return
//    } else {
//      return response;
//    }
//  },
//  error => {
//    return Promise.reject(error.response) // 返回接口返回的错误信息
//  });
export default axios;

main.js:

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
//开发环境下才会引入mockjs
// process.env.MOCK && require('@/mock')
// 新添加1
import ElementUI from 'element-ui'
// 新添加2,避免后期打包样式不同,要放在import App from './App';之前
import 'element-ui/lib/theme-chalk/index.css'
import App from './App'
import router from './router'
import store from './store'
import axios from '@/api/http'
import VueAxios from 'vue-axios'
Vue.use(VueAxios,axios)
// 新添加3
Vue.use(ElementUI)
Vue.config.productionTip = false
/* eslint-disable no-new */
window.vm = new Vue({
  el: '#app',
  router,
  store,
  data(){
    return{
      Bus:new Vue()
    }
  },
  components: { App },
  template: '<App/>'
})

展示结果如下:


最后Jwt简介+工具类应用+Jwt集成spa项目就到这里,祝大家在敲代码的路上一路通畅!

感谢大家的观看 !


目录
相关文章
|
15天前
|
安全 Java API
【三方服务集成】最新版 | 阿里云短信服务SMS使用教程(包含支持单双参数模板的工具类,拿来即用!)
阿里云短信服务提供API/SDK和控制台调用方式,支持验证码、通知、推广等短信类型。需先注册阿里云账号并实名认证,然后在短信服务控制台申请资质、签名和模板,并创建AccessKey。最后通过Maven引入依赖,使用工具类发送短信验证码。
【三方服务集成】最新版 | 阿里云短信服务SMS使用教程(包含支持单双参数模板的工具类,拿来即用!)
|
1月前
|
存储 Java 开发工具
【三方服务集成】最新版 | 阿里云OSS对象存储服务使用教程(包含OSS工具类优化、自定义阿里云OSS服务starter)
阿里云OSS(Object Storage Service)是一种安全、可靠且成本低廉的云存储服务,支持海量数据存储。用户可通过网络轻松存储和访问各类文件,如文本、图片、音频和视频等。使用OSS后,项目中的文件上传业务无需在服务器本地磁盘存储文件,而是直接上传至OSS,由其管理和保障数据安全。此外,介绍了OSS服务的开通流程、Bucket创建、AccessKey配置及环境变量设置,并提供了Java SDK示例代码,帮助用户快速上手。最后,展示了如何通过自定义starter简化工具类集成,实现便捷的文件上传功能。
【三方服务集成】最新版 | 阿里云OSS对象存储服务使用教程(包含OSS工具类优化、自定义阿里云OSS服务starter)
|
1月前
|
存储 JavaScript 数据库
ToB项目身份认证AD集成(一):基于目录的用户管理、LDAP和Active Directory简述
本文介绍了基于目录的用户管理及其在企业中的应用,重点解析了LDAP协议和Active Directory服务的概念、关系及差异。通过具体的账号密码认证时序图,展示了利用LDAP协议与AD域进行用户认证的过程。总结了目录服务在现代网络环境中的重要性,并预告了后续的深入文章。
|
1月前
|
人工智能 JavaScript 网络安全
ToB项目身份认证AD集成(三完):利用ldap.js实现与windows AD对接实现用户搜索、认证、密码修改等功能 - 以及针对中文转义问题的补丁方法
本文详细介绍了如何使用 `ldapjs` 库在 Node.js 中实现与 Windows AD 的交互,包括用户搜索、身份验证、密码修改和重置等功能。通过创建 `LdapService` 类,提供了与 AD 服务器通信的完整解决方案,同时解决了中文字段在 LDAP 操作中被转义的问题。
|
2月前
|
存储 中间件 API
ThinkPHP 集成 jwt 技术 token 验证
本文介绍了在ThinkPHP框架中集成JWT技术进行token验证的流程,包括安装JWT扩展、创建Token服务类、编写中间件进行Token校验、配置路由中间件以及测试Token验证的步骤和代码示例。
ThinkPHP 集成 jwt 技术 token 验证
|
1月前
|
jenkins Shell 持续交付
Jenkins持续集成GitLab项目 GitLab提交分支后触发Jenkis任务 持续集成 CI/CD 超级详细 超多图(二)
Jenkins持续集成GitLab项目 GitLab提交分支后触发Jenkis任务 持续集成 CI/CD 超级详细 超多图(二)
69 0
|
1月前
|
安全 Java 测试技术
ToB项目身份认证AD集成(二):快速搞定window server 2003部署AD域服务并支持ssl
本文详细介绍了如何搭建本地AD域控测试环境,包括安装AD域服务、测试LDAP接口及配置LDAPS的过程。通过运行自签名证书生成脚本和手动部署证书,实现安全的SSL连接,适用于ToB项目的身份认证集成。文中还提供了相关系列文章链接,便于读者深入了解AD和LDAP的基础知识。
|
1月前
|
Java Shell 开发工具
git集成IDEA,托管项目实现版本管理
git集成IDEA,托管项目实现版本管理
33 0
|
1月前
|
jenkins Shell 持续交付
Jenkins持续集成GitLab项目 GitLab提交分支后触发Jenkis任务 持续集成 CI/CD 超级详细 超多图(一)
Jenkins持续集成GitLab项目 GitLab提交分支后触发Jenkis任务 持续集成 CI/CD 超级详细 超多图(一)
143 0
|
2月前
|
存储 NoSQL 数据处理
组合和继承怎么集成一个性能较好的项目
组合与继承是面向对象编程的核心概念,前者通过对象间关联实现高效解耦,后者则重用代码以节省空间和内存。组合常用于现代项目,利用代理与依赖注入简化代码管理;而继承简化了子模块对父模块资源的应用,但修改会影响整体。随着分层解耦及微服务架构如SpringCloud的出现,这些技术进一步优化了数据处理效率和服务响应性能,尤其在分布式存储与高并发场景下。同步异步调用、Redis分布式应用等也广泛运用组合与继承,实现代码和内存空间的有效复用。