JWT和token介绍
什么是 Token?Token 是一种由服务器生成的字符串,通常用于标识用户的身份或会话状态。它可以在客户端和服务器之间传递,用于验证用户的身份或授权用户访问特定资源。
Token 的用途Token 的主要用途包括:
身份验证(Authentication):验证用户是否是他们声称的那个人。例如,用户登录时,服务器会生成一个 Token 并发送给客户端,客户端在后续请求中携带这个 Token,服务器通过验证 Token 来确认用户身份。
授权(Authorization):决定用户是否有权限访问某个资源。例如,某些 API 可能需要特定的 Token 才能访问。
会话管理(Session Management):在无状态的 HTTP 协议中,Token 可以用来跟踪用户的会话状态。
数据传输:Token 可以携带一些用户信息(如用户 ID、角色等),方便在请求中传递这些信息,而无需每次都查询数据库。
常见的 Token 类型
JWT(JSON Web Token)
定义:JWT 是一种开放标准(RFC 7519),用于在各方之间安全地传递信息。JWT 的信息可以被验证和信任,因为它是数字签名的。• 结构:JWT 由三部分组成,用点( . )分隔:
- Header(头部):通常包含令牌的类型(如 JWT)和使用的签名算法(如 HS256)。
- Payload(载荷):包含声明(Claims),这些声明是关于实体(通常是用户)和其他数据的声明。例如:
- sub (Subject):主题,通常是用户的唯一标识。
- exp (Expiration Time):过期时间。
- iat (Issued At):签发时间。• 自定义声明:如用户 ID、角色等。
- Signature(签名):用于验证消息的完整性和确保消息是由可信方发送的。
- 优点:
- 无状态:JWT 是自包含的,服务器不需要存储会话信息。
- 可扩展:可以携带用户信息,减少数据库查询。
- 跨域:可以跨不同的系统和平台使用。
- 缺点:
- 安全性:如果 Token 被泄露,攻击者可以伪造请求。因此,Token 必须通过 HTTPS 传输。
- 大小:JWT 可能比较大,不适合存储大量数据。
springBoot整合JWT实例:
package com.nie.utils;
import com.alibaba.druid.util.StringUtils;
import io.jsonwebtoken.*;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.util.Date;
@Data
@Component
@ConfigurationProperties(prefix = "jwt.token")
public class JwtHelper {
// JWT 令牌的有效时间,单位为毫秒(1000毫秒 = 1秒)
private long tokenExpiration;
// JWT 签名密钥,用于生成和验证 JWT 令牌
private String tokenSignKey;
/**
* 生成 JWT 令牌字符串。
* 这个方法根据用户 ID 创建一个 JWT 令牌,并设置其有效时间和签名。
*
* @param userId 用户的唯一标识
* @return 生成的 JWT 令牌字符串
*/
public String createToken(Long userId) {
System.out.println("tokenExpiration = " + tokenExpiration);
System.out.println("tokenSignKey = " + tokenSignKey);
String token = Jwts.builder()
.setSubject("YYGH-USER") // 设置主题(可以是任意字符串,用于标识令牌的用途)
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration * 1000 * 60)) // 设置令牌的有效时间(单位为分钟)
.claim("userId", userId) // 添加用户 ID 到 JWT 的自定义声明中
.signWith(SignatureAlgorithm.HS512, tokenSignKey) // 使用 HS512 算法和签名密钥对令牌进行签名
.compressWith(CompressionCodecs.GZIP) // 对 JWT 令牌进行压缩
.compact(); // 将 JWT 令牌转换为紧凑的字符串形式
return token;
}
/**
* 从 JWT 令牌字符串中获取用户 ID。
* 这个方法解析 JWT 令牌,并从其自定义声明中提取用户 ID。
*
* @param token JWT 令牌字符串
* @return 用户 ID,如果令牌无效或为空,则返回 null
*/
public Long getUserId(String token) {
if (StringUtils.isEmpty(token)) return null; // 如果令牌为空,直接返回 null
Jws<Claims> claimsJws = Jwts.parser() // 解析 JWT 令牌
.setSigningKey(tokenSignKey) // 设置签名密钥
.parseClaimsJws(token); // 解析并验证 JWT 令牌
Claims claims = claimsJws.getBody(); // 获取 JWT 的声明部分
Integer userId = (Integer) claims.get("userId"); // 从声明中提取用户 ID
return userId.longValue(); // 返回用户 ID 的长整型值
}
/**
* 判断 JWT 令牌是否过期。
* 这个方法解析 JWT 令牌,并检查其有效时间是否已经过期。
*
* @param token JWT 令牌字符串
* @return 如果令牌过期或无效,返回 true;否则返回 false
*/
public boolean isExpiration(String token) {
try {
boolean isExpire = Jwts.parser() // 解析 JWT 令牌
.setSigningKey(tokenSignKey) // 设置签名密钥
.parseClaimsJws(token) // 解析并验证 JWT 令牌
.getBody() // 获取 JWT 的声明部分
.getExpiration() // 获取令牌的有效时间
.before(new Date()); // 检查有效时间是否在当前时间之前
// 如果没有过期,返回 false
return isExpire;
} catch (Exception e) {
// 如果解析失败或令牌过期,返回 true
return true;
}
}
}
package com.nie.interceptors;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nie.utils.JwtHelper;
import com.nie.utils.Result;
import com.nie.utils.ResultCodeEnum;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class LoginProtectedInterceptor implements HandlerInterceptor {
// 自动注入 JwtHelper 工具类,用于处理 JWT 相关的逻辑
@Autowired
private JwtHelper jwtHelper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从请求头中获取名为 "token" 的值
String token = request.getHeader("token");
// 调用 JwtHelper 的 isExpiration 方法,检查 token 是否过期
boolean expiration = jwtHelper.isExpiration(token);
// 如果 token 未过期,允许请求继续执行
if (!expiration) {
return true;
}
// 如果 token 过期或无效,创建一个未登录的提示结果
Result result = Result.build(null, ResultCodeEnum.NOTLOGIN);
// 创建 ObjectMapper 对象,用于将 Result 对象转换为 JSON 格式的字符串
ObjectMapper objectMapper = new ObjectMapper();
// 将 Result 对象转换为 JSON 格式的字符串
String json = objectMapper.writeValueAsString(result);
// 将 JSON 字符串写入响应体,返回给客户端
response.getWriter().print(json);
// 返回 false,表示拦截请求,阻止请求继续执行
return false;
}
}
package com.nie.config;
import com.nie.interceptors.LoginProtectedInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private LoginProtectedInterceptor loginProtectedInterceptor;
@Override
/**
*
*添加拦截器
*/
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginProtectedInterceptor).addPathPatterns("/headline/**");
}
}
#jwt配置
jwt:
token:
tokenExpiration: 120 #有效时间,单位分钟
tokenSignKey: headline123456 #当前程序签名秘钥 自定义