SpringBoot整合JWT(JSON Web Token)生成token与验证

辛苦cv的小hui 2024-07-19 13:33:02 阅读 96

目录

JWT

什么是JWT

JWT使用流程

确定要传递的信息:

生成JWT:

JWT传输:

客户端保存JWT:

客户端发送JWT:

服务器验证JWT:

服务器响应:

Token的使用示例:

工具类

R结果集

返回一个生成的token

创建拦截器


JWT

什么是JWT

JWT(JSON Web Token)是是目前最流行的跨域认证解决方案。它通常被用于对用户进行身份验证和授权。JWT由三部分组成,每个部分之间使用"."进行分隔,这三部分分别是:

Header: 包含了声明类型和JWT的加密算法

Payload: 负载,存放有效信息的地方。这些有效信息包含三个部分:标准中注册的声明、公共的声明 和 私有的声明。

Signature: 签名信息。

官网地址:https://jwt.io/introduction

JWT使用流程

确定要传递的信息

首先,确定您想在JWT中传递的信息。这通常包括用户的唯一标识符(如用户ID)、角色、用户名等。

生成JWT
生成Header:Header是JWT的第一部分,它描述了JWT的类型(<code>"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存到当前线程.方便我们对发送请求的用户进行操作



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。