SpringBoot整合JWT(JSON Web Token)生成token与验证的流程及示例
作者:辛苦cv的小hui
JSON Web Token(JWT)是一种开放的标准(RFC 7519),定义了一种紧凑的、自包含的方式来安全地在各方之间传输信息作为JSON对象,这篇文章主要给大家介绍了关于SpringBoot整合JWT(JSON Web Token)生成token与验证的相关资料,需要的朋友可以参考下
JWT
什么是JWT
JWT(JSON Web Token)是是目前最流行的跨域认证解决方案。它通常被用于对用户进行身份验证和授权。JWT由三部分组成,每个部分之间使用"."进行分隔,这三部分分别是:
- Header: 包含了声明类型和JWT的加密算法
- Payload: 负载,存放有效信息的地方。这些有效信息包含三个部分:标准中注册的声明、公共的声明 和 私有的声明。
- Signature: 签名信息。
官网地址:https://jwt.io/introduction
JWT使用流程
确定要传递的信息:
- 首先,确定您想在JWT中传递的信息。这通常包括用户的唯一标识符(如用户ID)、角色、用户名等。
生成JWT:
- 生成Header:Header是JWT的第一部分,它描述了JWT的类型(
"typ"
)和加密算法("alg"
)。 - 生成Payload:Payload是JWT的第二部分,包含了编码后的信息。在Payload中,通常还会添加一个
"iat"
字段,表示JWT的签发时间(Issued At) - 生成Signature:使用一个密钥(Secret Key)对Header和Payload进行签名,以确保它们的完整性和真实性。签名是通过指定的算法(如HMAC SHA256)生成的。
- 将Base64编码后的Header、Payload和Signature用点号(.)连接起来,形成一个完整的JWT字符串。
- 例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWT传输:
- 服务器将生成的JWT发送给客户端,通常通过在HTTP响应的头部设置Authorization字段的值为
Bearer JWT
。
客户端保存JWT:
- 客户端在接收到JWT后,将其保存在本地,通常存储在Cookie、LocalStorage或SessionStorage中。
客户端发送JWT:
- 客户端在每次请求服务器时,将JWT带在请求的头部,通过Authorization字段将JWT发送给服务器。
- 请求头的格式通常为:
Authorization: Bearer JWT
服务器验证JWT:
- 服务器接收到请求后,从请求的头部中获取JWT,并进行验证。
- 验证的过程包括解析JWT、验证签名的完整性和真实性、检查JWT是否过期等。
- 如果验证通过,服务器会进一步解析Payload中的数据,获取用户的相关信息。
服务器响应:
- 如果JWT验证通过,服务器会继续处理请求,并返回相应的数据。
- 如果JWT验证失败,服务器会返回相应的错误信息。
通过以上流程,JWT实现了一种安全、紧凑、自包含的令牌传递机制,用于在客户端和服务器之间安全地传输用户信息。
springboot引入jwt相关依赖
要在Spring Boot项目中引入jjwt
,你需要在你的pom.xml
(如果你使用Maven)或build.gradle
(如果你使用Gradle)文件中添加相应的依赖。
maven:
<!--引入jwt相关包来生成token--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.2</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency>
Gradle:
dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.11.2' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.2' }
Token的使用示例:
工具类
先写一个创建Token的方法,再写一个验证token的方法
import com.certificateManage.common.R; import com.certificateManage.controller.exception.TokenException; import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.SignatureException; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.Date; //用于生成token的类 public class JwtTokenUtil { private static final String SECRET_KEY = "abcdefgabcdefghijklmnopqrstuvwxyz"; // 密钥 private static final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;//加密方式 //ttMillis是token持续时间 public static String createToken(String id, long ttlMillis) { // 签名密钥 byte[] secretKeyBytes = SECRET_KEY.getBytes(StandardCharsets.UTF_8); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyBytes, signatureAlgorithm.getJcaName()); // 设置JWT的签发时间和过期时间 Date now = new Date(); Date expiration = new Date(now.getTime() + ttlMillis); // 使用指定的密钥和算法生成JWT return Jwts.builder() .setSubject(id)//设置id .setIssuedAt(now) // 设置签发时间 .setExpiration(expiration) // 设置过期时间 .signWith(secretKeySpec,signatureAlgorithm) // 设置签名密钥和签名算法 .compact(); // 生成JWT字符串 } //验证token如果正确返回用户id public static R checkToken(String token){ try { // 解析token Claims claims = Jwts.parser() .setSigningKey(Keys.hmacShaKeyFor(SECRET_KEY.getBytes(StandardCharsets.UTF_8))) // 设置密钥 .parseClaimsJws(token) // 解析token .getBody(); // 获取负载 // 验证负载中的信息 String subject = claims.getSubject(); // 获取用户ID或其他信息 Date expiration = claims.getExpiration(); // 获取过期时间 System.out.println(expiration.toString()); // 验证token是否过期 if (expiration.before(new Date())) { throw new TokenException("token失效"); } return R.success(subject); } catch (ExpiredJwtException e) { // 当token过期时,会捕获到ExpiredJwtException异常 return R.error("Token已过期"); } catch (UnsupportedJwtException e) { // 当token不受支持时,会捕获到UnsupportedJwtException异常 return R.error("Token不受支持"); } catch (MalformedJwtException e) { // 当token格式错误时,会捕获到MalformedJwtException异常 return R.error("Token格式错误"); } catch (SignatureException e) { // 当token签名错误时,会捕获到SignatureException异常 return R.error("Token签名错误"); } catch (IllegalArgumentException e) { // 当token为空或非法时,会捕获到IllegalArgumentException异常 return R.error("Token为空或非法"); } catch (TokenException e) { // 处理TokenException return R.error("Token验证失败: " + e.getMessage()); } catch (Exception e) { // 处理其他异常 return R.error("发生未知错误: " + e.getMessage()); } } } // TokenException类,用于处理与Token相关的异常 public class TokenException extends Exception { public TokenException(String message) { super(message); } } }
R结果集
创建一个R结果集用来封装结果
//统一返回为结果集R 1为成功,0为失败 public class R<T> { private Integer code; //编码:1成功,0和其它数字为失败 private String msg; //错误信息 private T data; //数据 //静态方法返回成功时候,R的属性 public static <T> R<T> success(T object) { R<T> r = new R<>(); r.data = object; r.code = 1; return r; } //静态方法返回失败时传入消息 public static <T> R error(String msg) { R r = new R(); r.msg = msg; r.code = 0; return r; } // getter和setter方法省略... }
返回一个生成的token
在用户发送登录请求后如果验证通过就返回一个生成的token
String token=JwtTokenUtil.createToken(String.valueOf(user.getId()),3600000L);//生成token返回前端 return R.success(token);
接下来就是拦截所有请求并且验证响应头的token是否正确
- 请求头的格式通常为:
Authorization: Bearer JWT
线程工具类
在每次请求时在当前线程进行存储信息
public class BaseContext { // 使用ThreadLocal来存储用户ID private static final ThreadLocal<String> userIdThreadLocal = new ThreadLocal<>(); // 设置用户ID public static void setUserId(String userId) { userIdThreadLocal.set(userId); } // 获取用户ID public static String getUserId() { return userIdThreadLocal.get(); } // 清除用户ID(通常在请求处理完毕后调用) public static void clearUserId() { userIdThreadLocal.remove(); } }
创建拦截器
import com.certificateManage.common.BaseContext; import com.certificateManage.common.R; import com.certificateManage.util.tokenUtil.JwtTokenUtil; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 从请求头中获取JWT String token = request.getHeader("Authorization"); if ("OPTIONS".equalsIgnoreCase(request.getMethod())){ System.out.println("OPTIONS请求,放行"); return true; } if (token == null || !token.startsWith("Bearer ")) { // 如果没有JWT或者格式不正确,返回错误 response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "token令牌不存在或请求头格式错误"); return false; } // 去除"Bearer "前缀,获取真正的JWT token = token.substring(7); try { // 验证JWT R r=JwtTokenUtil.checkToken(token); // JWT验证成功,继续处理请求 if (r.getCode()==1) { // 将用户ID存储在ThreadLocal中 BaseContext.setUserId(String.valueOf(r.getData())); return true; }else { // JWT验证失败,返回错误 response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token验证失败"); return false; } } catch (Exception e) { // JWT验证失败,返回错误 response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token验证失败"); return false; } } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 在请求处理完毕后清除用户ID BaseContext.clearUserId(); } }
这样就可以在每次请求验证Token,并把token中存储的用户id存到当前线程.方便我们对发送请求的用户进行操作
总结
到此这篇关于SpringBoot整合JWT(JSON Web Token)生成token与验证的文章就介绍到这了,更多相关SpringBoot整合JWT生成token与验证内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!