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

news2024/10/6 11:44:36

目录

一、Jwt简介

1.1 Jwt是什么

1.2 为什么使用Jwt

 1.3 Jwt的工作原理

1.4 Jwt的组成

1.5 Jwt的验证过程

1.6 JWT令牌刷新思路

二、Jwt工具类 

2.1 Jwt工具类是什么

2.2 Jwt工具类的使用

 2.2.1 生成Jwt

2.2.2 解析Jwt

2.2.3 复制JWT并延时30分钟

2.2.4 测试JWT的有效时间

2.2.5 模拟过期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项目就到这里,祝大家在敲代码的路上一路通畅!

感谢大家的观看 !

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1089635.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

修复画笔工具组

修复画笔工具组 修复画笔工具组包括污点修复画笔工具、修复画笔工具、修补工具、内容感知移动工具和红眼工具&#xff0c;主要用于图像的修复或修补。 一、污点修复画笔工具 污点修复画笔工具可以去除图像中的污点、裂痕等不理想的部分&#xff0c;将其用与周围相似的图形来填充…

pc端使用微信扫码登录(思路篇)

我们在PC端网页中登录的时候有些需要微信扫码登录 例如CSDN网站登录 扫描之后 需要关注公众号 关注公众号就登录成功了 如何实现 流程图 步骤 1.pc端点击登录向业务服务器请求要登录二维码 2.业务服务器拿到用户端唯一参数或socketId&#xff08;使用websocket连接&#x…

10款精选的后台管理系统

1.vue2-manage 此项目是 vue element-ui 构建的后台管理系统&#xff0c;是后台项目node-elm 的管理系统&#xff0c;所有的数据都是从服务器实时获取的真实数据&#xff0c;具有真实的注册、登陆、管理数据、权限验证等功能。 项目地址&#xff1a;https://github.com/baili…

NSDT孪生编辑器助力智慧城市

技术有能力改变城市的运作方式&#xff0c;提高效率&#xff0c;为游客和居民提供更好的体验&#xff0c;实现更可持续的运营和更好的决策。 当今城市面临的主要挑战是什么&#xff0c;成为智慧城市如何帮助克服这些挑战&#xff1f; 我们生活在一个日益城市化的世界&#xf…

【Java 进阶篇】JavaScript Array数组详解

当我们编写JavaScript代码时&#xff0c;经常需要处理一组数据。JavaScript中的数组&#xff08;Array&#xff09;是一种用于存储多个值的数据结构&#xff0c;它提供了许多方法和功能&#xff0c;使我们能够方便地操作这些数据。在本篇博客中&#xff0c;我们将详细探讨JavaS…

【Linux初阶】多线程2 | 分离线程,线程库,线程互斥,可重入VS线程安全,锁的常见概念

文章目录 ☀️一、分离线程&#x1f33b;1.pthread_ self - 获取线程ID&#x1f33b;2.线程分离 ☀️二、用户级线程库&#x1f33b;1.pthread_t&#x1f33b;2.理解用户级线程库 - pthread库&#x1f33b;3.局部存储 ☀️三、线程互斥&#x1f33b;1.线程间的互斥相关概念&…

【Kali】简单记录

文章目录 信息收集DNS记录分析hostdigdnsenum 路由信息tcptraceroutetctrace 搜索引擎 目标识别arpingfping 识别操作系统p0f 服务枚举端口扫描nmap识别VPN服务器 漏洞映射exploitdbmsfconsole 提权arpspoofDsniff 信息收集 DNS记录分析 host host www.example.com host -a …

Windows 多媒体编程库 DirectX 介绍

目录 1、什么是DirectX&#xff1f; 2、使用DirectX的好处 2.1、DirectX为软件开发者提供硬件无关性 2.2、为硬件开发提供策略 3、DirectX的主体构成 3.1、Direct3D 3.2、DirectDraw 3.3、DirectPlay 3.4、DirectSound 3.5、DirectMusic 3.6、DirectInput 4、Dire…

Python数据分析实战-实现T检验(附源码和实现效果)

实现功能 T 检验&#xff08;Students t-test&#xff09;是一种常用的统计方法&#xff0c;用于比较两个样本之间的均值是否存在显著差异。它可以应用于许多场景&#xff0c;其中一些常见的应用场景包括&#xff1a; A/B 测试&#xff1a;在市场营销和用户体验研究中&#xf…

【mmdetection代码解读 3.x版本】FPN层的解读

文章目录 1. forward函数1.1 self.fpn_convs的构建 1. forward函数 def forward(self, inputs: Tuple[Tensor]) -> tuple:assert len(inputs) len(self.in_channels)# build laterals laterals [lateral_conv(inputs[i self.start_level])for i, lateral_conv in enumer…

QMidi Pro for Mac:打造您的专属卡拉OK体验

你是否曾经厌倦于在KTV里与朋友们争夺麦克风&#xff1f;是否想要在家中享受自定义的卡拉OK体验&#xff1f;现在&#xff0c;有了QMidi Pro for Mac&#xff0c;一切变得简单而愉快&#xff01; QMidi Pro是一款功能强大的卡拉OK播放器&#xff0c;专为Mac用户设计。它充分利…

机器学习(二)什么是机器学习

文章目录 什么是机器学习1.4.1确定是否为机器学习问题 1.5基于规则学习和基于模型的学习1.5.1基于规则学习1.5.2基于模型学习1.5.3房价预测问题 1.6机器学习数据的基本概念1.6.1机器学习数据集基本概念强化实践 后记 什么是机器学习 在开始讲解术语概念之前我们首先梳理下之前…

互联网Java工程师面试题·Java 并发编程篇·第三弹

目录 26、什么是线程组&#xff0c;为什么在 Java 中不推荐使用&#xff1f; 27、为什么使用 Executor 框架比使用应用创建和管理线程好&#xff1f; 27.1 为什么要使用 Executor 线程池框架 27.2 使用 Executor 线程池框架的优点 28、java 中有几种方法可以实现一个线程…

天猫用户重复购买预测(速通二)

天猫用户重复购买预测&#xff08;二&#xff09; 模型训练分类相关模型1、逻辑回归分类模型2、K近邻分类模型3、高斯贝叶斯分类模型4、决策树分类模型5、集成学习分类模型 模型验证模型验证指标 特征优化特征选择技巧1、搜索算法2、特征选择方法 模型训练 分类相关模型 1、逻…

基于Springboot实现商务安全邮箱邮件收发系统项目【项目源码+论文说明】

基于Springboot实现商务安全邮箱邮件收发系统演示 摘要 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。计算机的优势和普及使得商务安全邮箱的开发成为必需。 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;采用jsp技术…

Altium Designer | 5 - 网表导入及模块化布局设计(待续)

导入常见报错解决办法(unknow pin及绿色报错等) 在原理图界面 CtrlF搜索元器件位号 在PCB界面&#xff0c;CtrlF是左右翻转&#xff0c; 快捷键JC才是搜索元器件位号 报错信息&#xff1a; Unknow pin 1.没有封装 2.封装管脚缺失 3.元件库对应的管脚不对 ... 常见绿色报…

一键部署开源AI(人工智能对话模型)(支持显卡或CPU加内存运行)--ChatGLM2-6B

一、基本介绍&#xff1a; ChatGLM2-6B 是开源中英双语对话模型 ChatGLM-6B 的第二代版本&#xff0c;在保留了初代模型对话流畅、部署门槛较低等众多优秀特性的基础之上&#xff0c;ChatGLM2-6B 引入了如下新特性&#xff1a; 更强大的性能&#xff1a; 基于 ChatGLM 初代模…

盘点15个前端开源项目,yyds!

目录 1、vue-color-avatar2、Reader3、Ant Design4、小游戏2048&#xff08;Vue版&#xff09;5、跳一跳6、lifeRestart&#xff08;人生重开模拟器&#xff09;7、GOVIEW8、vlife9、网易云音乐 API10、饿了么11、QQ音乐 API12、ChatGPT API13、Node.js 最佳实践14、Awesome No…

云计算革命:多云管理与混合云的实践指南

文章目录 云计算的演进多云管理的优势1. 降低风险2. 提高性能3. 降低成本4. 提高安全性 实践指南1. 选择适当的云提供商2. 使用云管理平台3. 实施一致的安全策略4. 数据管理和迁移5. 自动化和编排 混合云的实践1. 私有云和本地数据中心2. 数据一致性3. 安全性和合规性4. 负载均…

IDEA启动报错Failed to create JVM. JVM path的解决办法

今天启动IDEA时IDEA报错&#xff0c;提示如下。 if you already hava a JDK installed, define a JAVA_HOME variable in Computer > Systen Properties > System Settings > Environment Variables.Failed to create JVM. JVM path:D:\ideaIU2023.2.3\IntelliJ IDE…