【SpringBoot】44、SpringBoot中整合JWT实现Token验证(整合篇)

【SpringBoot】44、SpringBoot中整合JWT实现Token验证(整合篇)什么是 JWT Jsonwebtoken JWT 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准 RFC7519 该 token 被设计为紧凑且安全的 特别适用于分布式站点的单点登录 SSO 场景 JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息 以便于从资源服务器获取资源 也可以增加一些额外的其它业务逻辑所必须的声明信息 该 token 也可直接被用于认证 也可被加密 为什么需要 JWT 当我们开发前后端分离项目时 要求我们对用户会话状态要进行一

什么是JWT?

Json web token (JWT),是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准((RFC 7519),该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

为什么需要JWT?

当我们开发前后端分离项目时,要求我们对用户会话状态要进行一个无状态处理,那我们知道普通的web项目用于管理用户会话的往往是 session,用户每次用服务器认证成功后,服务器都会发送一个 sessionid 给用户,session 是保存在服务端的,服务器通过 session 分辨用户,进行权限认证等一系列操作。每次请求完后,都会在 响应头中返回 sessionid 给浏览器,浏览器会将 sessionid 存储在 cookie 中,以后的每次请求都会在请求头中带上这个 sessionid 信息,服务器就会根据这个 sessionid 作为索引获取到具体的 session。

那上面讲述的场景就会出现一个痛点,当我们前后端分离后,我们的前端项目和后端项目分开部署,甚至会用到 nginx 来代理转发,也就是说前后端分离在应用解耦后增加了部署的复杂性。通常用户一次请求就要转发多次。如果用 session 每次携带 sessionid 到服务器,服务器还要查询用户信息。同时如果用户很多。这些信息存储在服务器内存中,给服务器增加负担。还有就是 CSRF(跨站伪造请求攻击)攻击,session 是基于 cookie 进行用户识别的, cookie 如果被截获,用户就会很容易受到跨站请求伪造的攻击。还有就是 sessionid 就是一个特征值,表达的信息不够丰富。不容易扩展。而且如果你后端应用是多节点部署。那么就需要实现 session 共享机制。不方便集群应用。

JWT的应用场景

JWT 就是上述痛点的解决方案之一,客户端在请求服务端进行登录操作时,服务端验证用户的账号和密码,验证成功后生成 token 返回给客户端,之后浏览器的每一次操作都会在请求头中带上这个 token,服务器会验证该 token 信息,验证成功后才会返回资源给浏览器。

JWT 的开销非常小,可以轻松在不同的域名中传递,所以在单点登录(SSO)中用到比较广泛,信息交换在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。

整合JWT

1、引入 JWT 依赖

 
   
   
   
     com.auth0 
    
   
     java-jwt 
    
   
     3.8.2 
    
   

2、JWT 工具类

  • 工具类中包含:创建 token,验证 token,获取用户 id 等
package com.asurplus.common.jwt; import cn.hutool.core.collection.CollectionUtil; import com.asurplus.common.utils.ResponseResult; import com.asurplus.common.utils.SpringContextUtils; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.JWTVerifier; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import javax.servlet.http.HttpServletRequest; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; / * Jwt工具类,生成JWT和认证 * * @author dongk * @date 2021-02-05 11:10:08 */ @Slf4j public class JwtUtil { / * 密钥 */ private static final String SECRET = "asurplus_secret"; / * 过期时间(单位:秒) / private static final long EXPIRATION = 3600L; / * 生成用户token,设置token超时时间 * * @param userId * @param password * @return */ public static String createToken(Integer userId, String account, String password) { Map 
  
    map = new HashMap<>(); map.put("alg", "HS256"); map.put("typ", "JWT"); String token = JWT.create() // 添加头部 .withHeader(map) // 放入用户的id .withAudience(String.valueOf(userId)) // 可以将基本信息放到claims中 .withClaim("account", account) .withClaim("password", password) // 超时设置,设置过期的日期 .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION * 1000)) // 签发时间 .withIssuedAt(new Date()) // SECRET加密 .sign(Algorithm.HMAC256(SECRET)); return token; } / * 获取用户id */ public static Integer getUserId() { HttpServletRequest request = SpringContextUtils.getHttpServletRequest(); // 从请求头部中获取token信息 String token = request.getHeader("Authorization"); if (StringUtils.isBlank(token)) { return null; } try { Algorithm algorithm = Algorithm.HMAC256(SECRET); JWTVerifier verifier = JWT.require(algorithm).build(); DecodedJWT jwt = verifier.verify(token); if (null != jwt) { // 拿到我们放置在token中的信息 List 
   
     audience = jwt.getAudience(); if (CollectionUtil.isNotEmpty(audience)) { return Integer.parseInt(audience.get(0)); } } } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (JWTVerificationException e) { e.printStackTrace(); } return null; } / * 校验token并解析token */ public static ResponseResult verity() { HttpServletRequest request = SpringContextUtils.getHttpServletRequest(); // 从请求头部中获取token信息 String token = request.getHeader("Authorization"); if (StringUtils.isBlank(token)) { return ResponseResult.error(401, "用户信息已过期,请重新登录"); } try { Algorithm algorithm = Algorithm.HMAC256(SECRET); JWTVerifier verifier = JWT.require(algorithm).build(); DecodedJWT jwt = verifier.verify(token); if (null != jwt) { // 拿到我们放置在token中的信息 List 
    
      audience = jwt.getAudience(); if (CollectionUtil.isNotEmpty(audience)) { return ResponseResult.success("认证成功", audience.get(0)); } } } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (JWTVerificationException e) { e.printStackTrace(); } return ResponseResult.error(401, "用户信息已过期,请重新登录"); } } 
     
    
  

我们可以看出 token 的创建用到了如下参数:

  • 算法:HS256
  • 类型:jwt
  • withAudience:向有效负载添加特定的受众(“aud”)声明,我们可以在这里放入一些用户的信息,例如:用户 id
  • withClaim:添加自定义索赔值,我们使用用户的账户和密码进行一起加密生成 jwt
  • withExpiresAt:超时时间设置,超时 token 将失效
  • withIssuedAt:签发时间,一般设置为当前时间
  • sign:签名,我们可以自定义签名和算法

JWT 的验证:

  • 首先我们先要获取 HttpServletRequest 请求对象,工具类放在下面了
  • 从请求头中获取 token 信息,根据 key(Authorization)获取 value 值
  • 然后使用签名进行算法加密得到 jwt 的验证对象,JWTVerifier.verify(token) 用来验证 token 的正确性
  • 我们还可以从验证得到的 DecodedJWT 对象中获取我们创建 token 的时候放入的信息,例如:用户 id

获取 HttpServletRequest 对象工具类

package com.asurplus.common.utils; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; / * SpringContext工具类 * * @Author Lizhou */ @Component public class SpringContextUtils implements ApplicationContextAware { / * 上下文对象实例 */ private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtils.applicationContext = applicationContext; } / * 获取applicationContext * * @return */ public static ApplicationContext getApplicationContext() { return applicationContext; } / * 获取HttpServletRequest */ public static HttpServletRequest getHttpServletRequest() { return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); } public static String getDomain() { HttpServletRequest request = getHttpServletRequest(); StringBuffer url = request.getRequestURL(); return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString(); } public static String getOrigin() { HttpServletRequest request = getHttpServletRequest(); return request.getHeader("Origin"); } } 

自定义 JSON 对象工具类

package com.asurplus.common.utils; import com.asurplus.common.enums.BaseEnums; import com.asurplus.common.enums.StatusEnums; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; / * 单例模式返回接口相应数据 * * @Author Lizhou */ @Data public class ResponseResult implements Serializable { @ApiModelProperty(value = "状态码") private Integer code; @ApiModelProperty(value = "提示信息") private String msg; @ApiModelProperty(value = "返回数据") private Object data; private static ResponseResult resultData(Integer code, String msg, Object data) { ResponseResult res = new ResponseResult(); res.setCode(code); res.setMsg(msg); res.setData(data); return res; } / * 成功 */ public static ResponseResult success() { return resultData(200, "操作成功", null); } / * 成功 */ public static ResponseResult success(String msg) { return resultData(200, msg, null); } / * 成功 */ public static ResponseResult success(Object data) { return resultData(200, "操作成功", data); } / * 成功 */ public static ResponseResult success(String msg, Object data) { return resultData(200, msg, data); } / * 失败 */ public static ResponseResult error() { return resultData(500, "操作失败", null); } / * 失败 */ public static ResponseResult error(Integer code) { return resultData(code, null, null); } / * 失败 */ public static ResponseResult error(Integer code, String msg) { return resultData(code, msg, null); } / * 失败 */ public static ResponseResult error(String msg) { return resultData(500, msg, null); } / * 失败 */ public static ResponseResult error(Object data) { return resultData(500, "操作失败", data); } / * 失败 */ public static ResponseResult error(Integer code, String msg, Object data) { return resultData(code, msg, data); } / * 失败 */ public static ResponseResult error(BaseEnums enums) { return resultData(enums.getCode(), enums.getMsg(), null); } } 

至此,我们在 SpringBoot 中整合 JWT 实现 Token 验证已经完成。

如您在阅读中发现不足,欢迎留言!!!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/223218.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午2:35
下一篇 2026年3月17日 下午2:35


相关推荐

  • 三大通信协议(二):IIC通信协议

    三大通信协议(二):IIC通信协议1.概念是什么?I²C(Inter-IntegratedCircuit),中文应该叫集成电路总线,它是一种串行通信总线,使用多主从架构,是由飞利浦公司在1980年代初设计的,方便了主板、嵌入式系统或手机与周边设备组件之间的通讯。由于其简单性,它被广泛用于微控制器与传感器阵列,显示器,IoT设备,EEPROM等之间的通信。优点仅需要两条总线即可通讯(大大的节约了IO口资源)最大主机数量:无限制。最大从机限制:理论127(一个主机多个从机,一对多,多对一,多对多)2.硬件连

    2022年5月5日
    123
  • Android App集成豆包大模型SDK实战:从接入到性能优化全指南

    Android App集成豆包大模型SDK实战:从接入到性能优化全指南

    2026年3月12日
    2
  • JMeter笔记1:聚合报告参数分析

    JMeter笔记1:聚合报告参数分析如下 Label 每个请求的名称 比如 HTTP 请求等 Samples 发给服务器的请求数量 如图是 200 个请求 若模拟 100 个用户 循环 10 次 请求数是 1000 Average 单个请求的平均响应时间 默认是单个 Request 的平均响应时间 当使用了 TransactionC 时 也可以以 Transaction 为单位显示平均响应时间 Median 中位数 也就是 50 用户的响应时间 9

    2026年3月17日
    2
  • Linux系统打不开gedit文本编辑器

    Linux系统打不开gedit文本编辑器写实验发现打不开gedit,报错如下:Unabletoinitserver:无法连接:拒绝连接(gedit:1289):Gtk-WARNIING**:20:46:00.562:cannotopendisplay于是收录各种gedit不能用原因如下(错误提示不一定和上面一样):原因1:vnc连接被禁用了。这种情况下一般虚拟机也没连上网(打开浏览器显示没网),打开虚拟机设置>选项>vnc连接,启用。具体参数设置可看链接(Vmware官网,将虚拟机配置为V..

    2022年7月26日
    58
  • Java基础知识学习笔记-12.1(待续)

    Java基础知识学习笔记-12.1(待续)

    2021年10月6日
    47
  • 局部性原理的理解

    局部性原理的理解局部性原理的理解杜逸闲本文首先介绍了局部性原理的定义 然后列举了一些局部性原理的应用 接着具体讨论了局部性原理在 pagecoloring 中的应用 最后分析了局部性原理的本质 什么是局部性原理在计算机学科的概念中 局部性原理是一个常用的术语 指处理器在访问某些数据时短时间内存在重复访问 某些数据或者位置访问的概率极大 大多数时间只访问局部的数据 主要可以分为时间局部性和空间局部性两种 时间局部性如果一个数据正在被访问 那么在近期它很可能还会被再次访问 任何编写过程序的人都

    2025年10月17日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号