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

简介: 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项目就到这里,祝大家在敲代码的路上一路通畅!

感谢大家的观看 !


目录
相关文章
|
2月前
|
前端开发 数据可视化 JavaScript
基于React的简易数据可视化图表库集成与应用
基于React的简易数据可视化图表库集成与应用
25 1
|
11天前
|
测试技术 持续交付 开发工具
《Git 简易速速上手小册》第6章:Git 在持续集成/持续部署(CI/CD)中的应用(2024 最新版)
《Git 简易速速上手小册》第6章:Git 在持续集成/持续部署(CI/CD)中的应用(2024 最新版)
33 2
|
1天前
|
敏捷开发 机器学习/深度学习 Java
Java中的异常处理机制深入理解与实践:持续集成在软件测试中的应用探索自动化测试在敏捷开发中的关键作用
【4月更文挑战第29天】在Java编程中,异常处理是一个重要的概念。它允许开发者在程序执行过程中遇到错误或异常情况时,能够捕获并处理这些异常,从而保证程序的稳定运行。本文将详细介绍Java中的异常处理机制,包括异常的分类、异常的处理方式以及自定义异常等内容。 【4月更文挑战第29天】 随着敏捷开发和DevOps文化的兴起,持续集成(CI)已成为现代软件开发周期中不可或缺的一环。本文将探讨持续集成在软件测试领域内的关键作用、实施策略以及面临的挑战。通过对自动化构建、测试用例管理、及时反馈等核心要素的详细分析,揭示持续集成如何提高软件质量和加速交付过程。 【4月更文挑战第29天】 在当今快速发
|
3天前
|
敏捷开发 测试技术 持续交付
深入探究持续集成在软件测试中的应用与优化
【4月更文挑战第28天】随着敏捷开发模式的普及,持续集成(Continuous Integration, CI)已成为软件开发工作流中不可或缺的一环。本文将深入探讨CI在软件测试领域的关键作用,分析其如何提升测试效率和质量,并指出实践中常见的挑战及解决策略。通过对自动化测试流程、测试驱动开发(TDD)以及持续部署(CD)等关键技术的综合运用,揭示了构建高效、可靠软件系统的方法论。
|
3天前
|
敏捷开发 Devops 测试技术
深入探究持续集成在软件测试中的应用与优化
【4月更文挑战第28天】随着敏捷开发和DevOps文化的兴起,持续集成(CI)已经成为现代软件开发不可或缺的一环。本文将探讨持续集成在软件测试领域的关键作用,分析其如何提高测试效率、确保产品质量,并指出实施过程中可能遇到的挑战及相应的解决策略。通过案例研究和最佳实践的分享,旨在为读者提供一套系统的持续集成优化方案,以支持更高效、更可靠的软件发布流程。
|
5天前
|
Java Docker 容器
SpringBoot项目集成XXL-job
SpringBoot项目集成XXL-job
|
16天前
|
安全 中间件 数据处理
中间件在应用集成
中间件是应用集成的关键,它连接不同系统、平台和应用,解决兼容性问题,实现数据交换和功能互操作。主要应用包括数据集成、服务集成、消息传递、安全与权限管理。选择中间件需考虑兼容性、性能、可扩展性和安全性。中间件简化通信,提高系统性能和可靠性,助力企业实现应用高效协同和商业价值。
17 2
|
16天前
|
缓存 Java Spring
单体项目中资源管理模块集成Spring Cache
该内容是关于将Spring Cache集成到资源管理模块以实现缓存同步的说明。主要策略包括:查询时添加到缓存,增删改时删除相关缓存。示例代码展示了@Service类中使用@Transactional和@Cacheable注解进行缓存操作,以及在RedisTemplate中处理缓存的示例。
24 5
|
18天前
|
JSON Kubernetes Go
无缝集成:在IntelliJ IDEA中利用Kubernetes插件轻松管理容器化应用
无缝集成:在IntelliJ IDEA中利用Kubernetes插件轻松管理容器化应用
28 0
无缝集成:在IntelliJ IDEA中利用Kubernetes插件轻松管理容器化应用
|
2月前
|
监控 Cloud Native 测试技术
持续集成与持续交付(CI/CD)在云原生环境中的应用与优化
传统软件开发模式下的集成和交付流程往往繁琐且易出错,而随着云原生技术的快速发展,持续集成与持续交付(CI/CD)在云原生环境中的应用变得尤为重要。本文将探讨CI/CD在云原生环境中的应用及优化策略,包括自动化测试、容器化部署以及监控和反馈机制等方面,旨在帮助开发团队更好地应对云原生时代的挑战。
19 2