当前位置: 首页 > news >正文

分布式环境集成JWT(Java Web Token)

目录

  • 一,说明:
  • 二,Token、Session和Cookie比较
  • 三,Spring Boot项目集成JWT
    • 1,引入依赖
    • 2,Token工具类
    • 3,定义拦截器
    • 4,注册拦截器
    • 5,编写登录代码
    • 6,测试
  • 四,说明

一,说明:

  • Token的引入:客户端向服务端请求数据时一般都会加入验证信息,比如客户端在请求的信息中携带用户名、密码,服务端会校验用户名和密码是否正确,校验通过响应该客户端请求。但是每次都携带用户名和密码无疑有些繁琐,而且也不安全,在这种背景下,Token便应运而生。Token在计算机身份认证中是令牌的意思。
  • Token的定义:Token是服务端生成的一串字符串,用来作为客户端请求的一个令牌。Token是客户端第一次登录时,由服务端生成并将其返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需带上用户名和密码。

二,Token、Session和Cookie比较

  • Session和Cookie区别:
    • 数据存放位置不同:Session数据是存在服务器中的,Cookie数据存放在浏览器当中;
    • 安全程度不同:Session存储在服务器中,比Cookie存储在浏览器中,安全程度要高;
    • 性能使用程度不同:Session存储在服务器上,数量过多会影响服务器性能;
    • 数据存储大小不同:单个Cookie保存的数据不能超过4K,Session存储在服务端,根据服务器大小决定。
  • Token和Session区别:
    • Token是开发定义的,Seesion是http协议规定的;
    • Token一般不存储,Session存储在服务器中,且在分布式环境中,Session会失效;

三,Spring Boot项目集成JWT

  • 我们实现如下功能:客户端登录获取Token,请求时携带Token,服务端对携带的Token进行验证,若是有效Token则放行,非法或是过期Token拦截,给出说明信息后直接返回。
  • 代码中抛出的异常,以及返回的实体类信息,是自定义异常,详细可参考该博客自定义异常。

1,引入依赖

    <!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency><!---jwt(java web token)--><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId></dependency>

2,Token工具类

通过该工具类来生成Token,以及从请求头中获取token来获取当前用户的信息。

package com.tick.tack.utils;import cn.hutool.core.date.DateUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.tick.tack.manager.entity.User;
import com.tick.tack.manager.service.IUserService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;@Component
public class TokenUtils {// 日志类private static final Logger log= LoggerFactory.getLogger(TokenUtils.class);private static IUserService staticUserService;@Resourceprivate IUserService userService;@PostConstructpublic void setUserService() {//必须加@Component注解后才会执行该段代码,在spring容器中初始化staticUserService = userService;}public static String getToken(String userId, String password) {return JWT.create().withAudience(userId) //将userId保存到token里面,作为载荷.withExpiresAt(DateUtil.offsetHour(new Date(), 2))//2小时候过期.sign(Algorithm.HMAC256(password));//以password作为token的密钥}/*** 获取当前登录的用户信息*/public static User getCurrentUser() {try {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();// 从请求头中获取token信息String token = request.getHeader("token");if (StringUtils.isNotBlank(token)) {String userAccount = JWT.decode(token).getAudience().get(0);return staticUserService.queryUserByAccount(userAccount);}} catch (Exception e) {e.printStackTrace();}return null;}
}

3,定义拦截器

AuthAccess是一个自定义的注解,在拦截器中判断如果方法上有加入该注解,则放行,不校验token

package com.tick.tack.common.interceptor;import cn.hutool.jwt.JWTException;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.tick.tack.common.Constants;
import com.tick.tack.config.AuthAccess;
import com.tick.tack.exception.ServiceException;
import com.tick.tack.manager.entity.User;
import com.tick.tack.manager.service.IUserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class JWTInterceptor implements HandlerInterceptor {@Autowiredprivate IUserService userService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String token = request.getHeader("token");//如果不是映射到方法直接通过if (!(handler instanceof HandlerMethod)) {return true;} else {// 判断是否为自定义注解AuthAccess,如果是,就不校验了,直接放行HandlerMethod h = (HandlerMethod) handler;AuthAccess authAccess = h.getMethodAnnotation(AuthAccess.class);if (authAccess != null) {return true;}}//执行认证if (StringUtils.isBlank(token)) {throw new ServiceException(Constants.CODE_401, "无token,请重新登录");}//获取token中的user id,验证是否合法String userAccount;try {userAccount = JWT.decode(token).getAudience().get(0);} catch (JWTException jwt) {throw new ServiceException(Constants.CODE_401, "token验证失败");}//根据token中的用户账号查询数据库信息User user = userService.queryUserByAccount(userAccount);if (user == null) {throw new ServiceException(Constants.CODE_401, "用户不存在,请重新登录");}//用户密码加签验证tokenJWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();try {//通过 verifier.verify() 方法检验 token,如果token不符合则抛出异常jwtVerifier.verify(token);} catch (Exception e) {throw new ServiceException(Constants.CODE_401, e.getMessage());}return true;}
}

自定义注解:

package com.tick.tack.config;import java.lang.annotation.*;@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthAccess {
}

4,注册拦截器

将拦截器注册到SpringMVC中

package com.tick.tack.config;import com.tick.tack.common.interceptor.JWTInterceptor;
import org.springframework.context.annotation.Bean;
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 InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtInterceptor())//拦截的路径.addPathPatterns("/**") //拦截所有请求,通过判断token是否合法来决定是否需要登录//排除不校验的接口.excludePathPatterns("/loginUser", //排除路径的时候不用考虑全局上下文context-path"/register",//Swagger页面拦截取消"/swagger-resources/**", "/webjars/**", "/v3/**", "/swagger-ui.html/**", "doc.html", "/error");}//考虑到UserService,此处需要注入一下@Beanpublic JWTInterceptor jwtInterceptor() {return new JWTInterceptor();}
}

5,编写登录代码

  • 1,登录实体类
@Data
public class LoginUser {// 登入用户名private String userAccount;// 登录密码private String password;
}
  • 2,token实体类
@Data
public class TickToken {// 用户名private String userAccount;// 密码private String password;// tokenprivate String token;// 到期时间private Date expireTime;
}
  • 3,系统登录控制类
@RestController
public class LoginController {@Autowired //按照类型注入@Qualifier(value = "loginServiceImpl")private ILoginService ILoginService;//登录系统@PostMapping("/loginUser")public Result loginSystem(@RequestBody LoginUser user) {if (StringUtils.isBlank(user.getUserAccount()) || StringUtils.isBlank(user.getPassword())) {return Result.error(Constants.CODE_400, "参数错误");}TickToken tickToken = ILoginService.loginSystem(user);return Result.success(tickToken);}
}
  • 4,业务逻辑实现类
public TickToken loginSystem(LoginUser user) {User one = userService.queryUserByAccount(user.getUserAccount());if (one != null && one.getPassword().equals(user.getPassword())) {TickToken tickToken = new TickToken();//生成token信息并返回String token = TokenUtils.getToken(one.getUserAccount(), one.getPassword());tickToken.setToken(token);// 设置过期时间:当前时间两小时以后tickToken.setExpireTime(DateUtil.offsetHour(new Date(),2));// 处理用户的菜单信息,在登录的时候返回给用户List<Menu> roleMenus = getRoleMenus(one);//tickToken.setMenus(roleMenus);return tickToken;} else {throw new ServiceException(Constants.CODE_600, "用户名或密码错误");}}

6,测试

  • 1,测试拦截
    当前未登录,测试拦截是否生效
@RestController
@RequestMapping("/demo")
public class DemoController {@GetMapping("/{id}")//@AuthAccesspublic Result getUser(@PathVariable("id") Integer id) {User user = new User(1, "zhangSan");return Result.success(user);}
}

未登录,也没有加相应注解,会提示没有token信息。
在这里插入图片描述

  • 2,登录获取token
    在这里插入图片描述* 3,携带token请求
    在这里插入图片描述
    如部分接口不希望被拦截,则为该接口方法加上@AuthAccess注解即可。

四,说明

该token是基于账户和密码来生成的一串字符串,并指定了过期时间,假如登录请求是在A机器实现,下一次请求在经过负载均衡后负载到B机器,B机器也可对其验证,因为token已经包括了全部的验证信息,服务器不保存相关信息,这样在分布式环境下也可正常使用。

相关文章:

分布式环境集成JWT(Java Web Token)

目录 一&#xff0c;说明&#xff1a;二&#xff0c;Token、Session和Cookie比较三&#xff0c;Spring Boot项目集成JWT1&#xff0c;引入依赖2&#xff0c;Token工具类3&#xff0c;定义拦截器4&#xff0c;注册拦截器5&#xff0c;编写登录代码6&#xff0c;测试 四&#xff…...

Python实战之数据表提取和下载自动化

在网络爬虫领域&#xff0c;动态渲染类型页面的数据提取和下载自动化是一个常见的挑战。本文将介绍如何利用Pyppeteer库完成这一任务&#xff0c;帮助您轻松地提取动态渲染页面中的数据表并实现下载自动化。 一、环境准备 首先&#xff0c;确保您已经安装了Python环境。接下来…...

Midjourney学习(三)6个高级应用

使用Remix Mode在原图片的基础上进行二次创作 通过prompt得到大图之后&#xff0c;点击Make Variations按钮&#xff0c;输入Remix Prompt&#xff0c;即可得到意想不到的效果&#xff01; 局部内容重绘 通过局部重绘可以实现对画面内容更加精细化的控制&#xff0c;同样也是需…...

C语言:指针类型的意义

1.指针的类型决定了解引用时访问几个字节 2.指针的类型决定了指针1、-1跳过几个字节 一、指针的类型决定指针解引用时访问几个字节 例如 int 型指针解引用时访问4个字节 char 型指针解引用时访问1个字节 详解代码如下&#xff1a; int b 0x11223344&#xff08;十六进制&…...

如何将 PDF 转换为 Word:前 5 个应用程序

必须将 PDF 转换为 Word 才能对其进行编辑和自定义。所以这里有 5 种很棒的方法 PDF 文件被广泛使用&#xff0c;因为它非常稳定且难以更改。这在处理法律合同、财务文件和推荐信等重要文件时尤其重要。但是&#xff0c;有时您可能需要编辑 PDF 文件。最好的方法是使用应用程序…...

AP5192 DC-DC降压恒流LED汽车头灯摩托车电动车大灯电源驱动

AP5192是一款PWM工作模式,高效率、外围简单、 内置功率MOS管&#xff0c;适用于4.5-100V输入的高精度 降压LED恒流驱动芯片。最大电流1.5A。 AP5192可实现线性调光和PWM调光&#xff0c;线性调光 脚有效电压范围0.55-2.6V. AP5192 工作频率可以通过RT 外部电阻编程 来设定&…...

Python Opencv实践 - Canny边缘检测

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_GRAYSCALE) print(img.shape)#图像Canny边缘检测 #cv.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradien…...

Python编程练习与解答 练习119:低于和高于平均水平

编写一个程序&#xff0c;从用户处读取数字&#xff0c;直到用户输入空行。程序应该显示用户输入的所有值的平均值。然后所有程序应该显示所有平均值的值&#xff0c;然后显示所有平均值&#xff08;若有&#xff09;&#xff0c;最后显示所有高于平均值的值。再每个值列表之前…...

vue中的nextTick的作用

vue里面&#xff0c;常用的事件onMounted里&#xff0c;总喜欢用一个nextTick&#xff1a; onMounted(() > {nextTick(() > {init();}); });这个东西有啥用呢&#xff1f;我总搞不懂。 今天我忽然有点明白了。这是一个跟前面语句有关的方法。意思是&#xff0c;等前面的…...

如何通过四个步骤清理网络防火墙规则

组织必须确保适当的安全策略到位&#xff0c;以保护其投资并优化其安全有效性。然而&#xff0c;随着网络的扩展和复杂性的增加&#xff0c;网络运营团队面临着管理来自多个供应商的大量防火墙和网络设备的挑战。他们必须解决分散的基础设施、职能孤岛、人员配置问题、分散的管…...

打开谷歌浏览器远程调试功能

谷歌浏览器远程调试功能 首先我们来启动Chrome的远程调试端口。你需要找到Chrome的安装位置&#xff0c;在Chrome的地址栏输入chrome://version就能找到Chrome的安装路径 开启远程控制命令 文件路径/chrome.exe --remote-debugging-port9222开启后的样子(注意要关闭其他谷歌浏…...

ChatGPT时代的我的博客

好久没有在CSDN写原创文章了。 ChatGPT出来之后&#xff0c;肯定对CSDN这样的平台有很大的冲击性。 我平时在CSDN写的文章&#xff0c;大多是翻译和一些平时编程遇到的代码问题。小部分是一些自己的经验和总结。 这些文章会被ChatGPT&#xff0c;或者更通用的说&#xff0c;…...

同步有关的思考。

同步通常标志着系统处于不稳定状态&#xff0c;所以同步过程分析和控制是非常重要的。 高速同步过程&#xff1a;高速同步的前提是同步源数据稳定可靠且同步过程不会破坏同步源数据的稳定性&#xff0c;数据接收方资源需要有足够裕量且能维持在就绪状态&#xff0c;双方连接链…...

Flutter Web 项目网络请求报 XMLHttpRequest error 解决方案

使用http库进行简单的网络请求时&#xff0c;运行在Chrome浏览器上&#xff0c;网络请求一直报错 XMLHttpRequest error&#xff0c;而在iOS 模拟器上运行则正常&#xff0c;后面在postman上发送请求&#xff0c;也是正常的。这就是很尴尬了&#xff01;&#xff01;&#xff0…...

Python面试:什么是GIL

1. GIL (Global Interpreter lock)可以避免多个线程同时执行字节码。 import threadinglock threading.Lock()n [0]def foo():with lock:n[0] n[0] 1n[0] n[0] 1threads [] for i in range(5000):t threading.Thread(targetfoo)threads.append(t)for t in threads:t.s…...

idea --Git Commit Template插件

Git Commit Template是一款免费的IntelliJ IDEA插件&#xff0c;用于提供Git提交模板。该插件可以帮助开发者编写规范的Git提交信息&#xff0c;提高代码管理效率。 首先安装插件&#xff1a; 使用Git Commit Template插件: 注&#xff1a;long description和Breaking changes…...

使用Python脚本添加新的相关节点到arxml文件中的指定位置

使用Python脚本添加新的相关节点到arxml文件中的指定位置 1 背景 随着汽车软件开发的复杂度越来越高,链路越来越长,很多手动配置的工具链所需要的时间就会被拉长,显然这对于项目的开发进度有了一定影响,根据需求自动化生成arxml文件其实很有必要。同时越来越多的测试开始…...

iOS开发Swift-闭包

1.闭包表达式语法 { (参数) -> return 类型 in//内容 }let names ["C", "A", "E", "B", "D"] func back(_ s1: String, s2: String) -> Bool {return s1 > s2 //(B > A, C > B) }//闭包后&#xff1a; va…...

从零开始学JAVA——常用类

常用类 课后习题一&#xff1a;课后练习二&#xff1a;课后练习三&#xff1a;课后练习四课后练习五&#xff1a; 课后习题一&#xff1a; 将字符串“2016-02-22”转换为对应的java.sql.Date类的对象 SimpDateFormat sdf new SimpDateFormate(“yyyy-MM-DD”); 解析 java.ut…...

LeetCode 面试题 02.04. 分割链表

文章目录 一、题目二、C# 题解 一、题目 给你一个链表的头节点 head 和一个特定值 x&#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你不需要 保留 每个分区中各节点的初始相对位置。 点击此处跳转题目。 示例 1&#…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...