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

JWT实现单点登录

文章目录

  • JWT实现单点登录
    • JWT 简介
    • 存在问题及解决方案
    • 登录流程
      • 后端程序实现
      • 前端保存Token
      • store存放信息的缺点及解决
    • 校验流程:为gateway增加登录校验拦截器
  • 另一种单点登录方法:Token+Redis实现单点登录

JWT实现单点登录

  • 登录流程:
    校验用户名密码->生成随机JWT Token->返回给前端。之后前端发请求携带该Token就能验证是哪个用户了。
  • 校验流程:
    从前端请求的header获取JWT Token->根据工具包校验JWT Token->校验成功或失败

JWT 简介

结构
Header 头部信息,主要声明了JWT的签名算法等信息
Payload 载荷信息,主要承载了各种声明并传递明文数据
Signature 签名,拥有该部分的JWT被称为JWS,也就是签了名的JWT,用于校验数据
整体结构是:

header.payload.signature

参考文档:https://doc.hutool.cn/pages/jwt/

存在问题及解决方案

    1. token被解密:如工具包被获取。可通过增加“盐值”来解决。
    1. token被拿到第三方使用:如被包装到第三方使用(ChatGPT工具),可以通过限流来解决。

登录流程

后端程序实现

封装hutool工具类:

public class JwtUtil {private static final Logger LOG = LoggerFactory.getLogger(JwtUtil.class);/*** 盐值很重要,不能泄漏,且每个项目都应该不一样,可以放到配置文件中*/private static final String key = "xxx";public static String createToken(Long id, String mobile) {LOG.info("开始生成JWT token,id:{},mobile:{}", id, mobile);GlobalBouncyCastleProvider.setUseBouncyCastle(false);DateTime now = DateTime.now();DateTime expTime = now.offsetNew(DateField.HOUR, 24);
//        DateTime expTime = now.offsetNew(DateField.SECOND, 10);Map<String, Object> payload = new HashMap<>();// 签发时间payload.put(JWTPayload.ISSUED_AT, now);// 过期时间payload.put(JWTPayload.EXPIRES_AT, expTime);// 生效时间payload.put(JWTPayload.NOT_BEFORE, now);// 内容payload.put("id", id);payload.put("mobile", mobile);String token = JWTUtil.createToken(payload, key.getBytes());LOG.info("生成JWT token:{}", token);return token;}public static boolean validate(String token) {LOG.info("开始JWT token校验,token:{}", token);GlobalBouncyCastleProvider.setUseBouncyCastle(false);JWT jwt = JWTUtil.parseToken(token).setKey(key.getBytes());// validate包含了verifyboolean validate = jwt.validate(0);LOG.info("JWT token校验结果:{}", validate);return validate;}public static JSONObject getJSONObject(String token) {GlobalBouncyCastleProvider.setUseBouncyCastle(false);JWT jwt = JWTUtil.parseToken(token).setKey(key.getBytes());JSONObject payloads = jwt.getPayloads();payloads.remove(JWTPayload.ISSUED_AT);payloads.remove(JWTPayload.EXPIRES_AT);payloads.remove(JWTPayload.NOT_BEFORE);LOG.info("根据token获取原始内容:{}", payloads);return payloads;}public static void main(String[] args) {createToken(1L, "123");String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYmYiOjE3MzY0ODczMDQsIm1vYmlsZSI6IjEyMyIsImlkIjoxLCJleHAiOjE3MzY1NzM3MDQsImlhdCI6MTczNjQ4NzMwNH0.Bui7guCvPEF557eqxRLwmt5tO-W-3oVLnn37H4qOVfA";validate(token);getJSONObject(token);}
}

后端定义登录业务:

    public MemberLoginResp login(MemberLoginReq memberLoginReq){String mobile = memberLoginReq.getMobile();String code = memberLoginReq.getCode();Member memberDB = selectByMobile(mobile);if (ObjectUtil.isEmpty(memberDB)){throw new BusinessException(BusinessExceptionEnum.MEMBER_MOBILE_NOT_EXIST);}if(!code.equals("8888")){throw new BusinessException(BusinessExceptionEnum.MEMBER_MOBILE_CODE_ERROR);}MemberLoginResp memberLoginResp = new MemberLoginResp();memberLoginResp.setId(memberDB.getId());memberLoginResp.setMobile(mobile);String token = JwtUtil.createToken(memberDB.getId(), memberDB.getMobile());memberLoginResp.setToken(token);return memberLoginResp;}

通过调用封装的JwtUtil生成token并返回前端

在这里插入图片描述

成功返回Token结果

前端保存Token

Vuex全局保存Token到store中

import { createStore } from 'vuex'const MEMBER = "MEMBER";export default createStore({state: {member: {}},getters: {},mutations: {setMember (state, _member) {state.member = _member;}},actions: {},modules: {}
})
    const login = () => {axios.post("/member/member/login", loginForm).then((response) => {let data = response.data;if (data.success) {notification.success({ description: '登录成功!' });// 登录成功,跳到控台主页router.push("/welcome");store.commit("setMember", data.content);} else {notification.error({ description: data.message });}})};

store存放信息的缺点及解决

store存放用户信息后,如果刷新页面,那么信息也会消失!
store可以理解为缓存,一旦重新加载,则缓存全都没了。

解决方法:

  • step1. 新增session-storage.js,封装会话缓存sessionStorage
// 所有的session key都在这里统一定义,可以避免多个功能使用同一个key
SESSION_ORDER = "SESSION_ORDER";
SESSION_TICKET_PARAMS = "SESSION_TICKET_PARAMS";SessionStorage = {get: function (key) {var v = sessionStorage.getItem(key);if (v && typeof(v) !== "undefined" && v !== "undefined") {return JSON.parse(v);}},set: function (key, data) {sessionStorage.setItem(key, JSON.stringify(data));},remove: function (key) {sessionStorage.removeItem(key);},clearAll: function () {sessionStorage.clear();}
};
  • step2. 在index.html中引入该js
<!DOCTYPE html>
<html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1.0"><link rel="icon" href="<%= BASE_URL %>favicon.ico"><!-- 引入js --><script src="<%= BASE_URL %>js/session-storage.js"></script><title><%= htmlWebpackPlugin.options.title %></title></head><body><noscript><strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><!-- built files will be auto injected --></body>
</html>
  • step3. 修改store的index.js
const MEMBER = "MEMBER";export default createStore({state: {member: window.SessionStorage.get(MEMBER) || {} # 读取},getters: {},mutations: {setMember (state, _member) {state.member = _member;window.SessionStorage.set(MEMBER, _member); # 设置}},

不再是把member定义为{},而是首先在缓存中获取,如果没有则设置为{}。同时避免空指针
同时在用户登录后设置MEMBER缓存

校验流程:为gateway增加登录校验拦截器

  • 添加依赖
            <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.10</version></dependency>
  • 拦截器类
@Component
public class LoginMemberFilter implements Ordered, GlobalFilter {private static final Logger LOG = LoggerFactory.getLogger(LoginMemberFilter.class);@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String path = exchange.getRequest().getURI().getPath();// 排除不需要拦截的请求if (path.contains("/admin")|| path.contains("/redis")|| path.contains("/test")|| path.contains("/member/member/login")|| path.contains("/member/member/send-code")) {LOG.info("不需要登录验证:{}", path);return chain.filter(exchange);} else {LOG.info("需要登录验证:{}", path);}// 获取header的token参数String token = exchange.getRequest().getHeaders().getFirst("token");LOG.info("会员登录验证开始,token:{}", token);if (token == null || token.isEmpty()) {LOG.info( "token为空,请求被拦截" );exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}// 校验token是否有效,包括token是否被改过,是否过期boolean validate = JwtUtil.validate(token);if (validate) {LOG.info("token有效,放行该请求");return chain.filter(exchange);} else {LOG.warn( "token无效,请求被拦截" );exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}}/*** 优先级设置  值越小  优先级越高** @return*/@Overridepublic int getOrder() {return 0;}
}
  • 测试结果:
  1. 直接调用不需要验证登录的接口
@RestController
public class TestController {@GetMapping("/test")public String test(){return "test";}}

在这里插入图片描述

  1. 调用需要登录的接口方法(未登录)
    在这里插入图片描述

同时服务器端没有打印,表示请求已被拦截

  1. 调用login登陆后再次执行上述请求
    login打印日志:
    在这里插入图片描述
    调用请求打印日志:
    在这里插入图片描述

可见成功校验token,并读取登录用户信息,通过校验

另一种单点登录方法:Token+Redis实现单点登录

  • 登录流程:
    校验用户名密码->生成随机Token->将Token存放到Redis,并返回给前端。
    之后前端发请求携带该Token就能验证是哪个用户了。
  • 校验流程:
    从前端请求的header获取Token->根据Token到Redis获取用户数据->若有数据则登录校验通过,否则失败

相关文章:

JWT实现单点登录

文章目录 JWT实现单点登录JWT 简介存在问题及解决方案登录流程后端程序实现前端保存Tokenstore存放信息的缺点及解决 校验流程&#xff1a;为gateway增加登录校验拦截器 另一种单点登录方法&#xff1a;Token&#xff0b;Redis实现单点登录 JWT实现单点登录 登录流程&#xff…...

云计算的概念与特点:开启数字化时代的新篇章

在当今数字化时代,云计算(Cloud Computing)已经成为推动技术创新和业务转型的核心力量。无论是大型企业、中小型企业,还是个人用户,云计算都为其提供了高效、灵活和经济的解决方案。本文将深入探讨云计算的概念及其核心特点,帮助读者全面了解这一革命性技术。 © ivw…...

salesforce中如何获取一个profile的18位id

在 Salesforce 中&#xff0c;要获取一个 Profile 的 18 位 ID&#xff0c;可以通过以下几种方式实现&#xff1a; 方法 1&#xff1a;通过 Developer Console 登录 Salesforce。 点击右上角的 头像 或 设置齿轮&#xff0c;选择 “开发者控制台”&#xff08;Developer Conso…...

Vue 3 中的标签 ref 与 defineExpose:模板引用与组件暴露

在 Vue 3 中&#xff0c;ref 不仅可以用于创建响应式数据&#xff0c;还可以用于获取 DOM 节点或组件实例。通过 ref&#xff0c;我们可以直接访问模板中的元素或组件&#xff0c;并在需要时操作它们。此外&#xff0c;defineExpose 用于在 <script setup> 语法中显式暴露…...

FLTK - FLTK1.4.1 - demo - adjuster.exe

文章目录 FLTK - FLTK1.4.1 - demo - adjuster.exe概述笔记根据代码&#xff0c;用fluid重建一个adjuster.fl 备注 - fluid生成的代码作为参考代码好了修改后可用的代码END FLTK - FLTK1.4.1 - demo - adjuster.exe 概述 想过一遍 FLTK1.4.1的demo和测试工程&#xff0c;工程…...

单路由及双路由端口映射指南

远程登录总会遇到登陆不上的情况&#xff0c;可能是访问的大门没有打开哦&#xff0c;下面我们来看看具体是怎么回事&#xff1f; 当软件远程访问时&#xff0c;主机需要两个条件&#xff0c;一是有一个唯一的公网IP地址&#xff08;运营商提供&#xff09;&#xff0c;二是开…...

专为课堂打造:宏碁推出三款全新耐用型 Chromebook

IT之家 1 月 25 日消息&#xff0c;宏碁&#xff08;Acer&#xff09;昨日&#xff08;1 月 24 日&#xff09;发布公告&#xff0c;针对教育市场&#xff0c;推出 Chromebook Spin 512 (R857T)、Chromebook Spin 511 (R757T) 和 Chromebook 511 (C737) 三款产品&#xff0c;兼…...

云计算架构学习之LNMP架构部署、架构拆分、负载均衡-会话保持

一.LNMP架构部署 1.1. LNMP服务搭建 1.磁盘信息 2.内存 3.负载信息 4.Nginx你们公司都用来干嘛 5.文件句柄(文件描述符 打开文件最大数量) 6.你处理过系统中的漏洞吗 SSH漏洞 7.你写过什么shell脚本 8.监控通过什么告警 zabbix 具体监控哪些内容 9.mysql redis查询 你好H…...

Python案例--暂停与时间格式化

在编程中&#xff0c;时间的处理是一个常见的需求。无论是日志记录、任务调度还是数据时间戳的生成&#xff0c;正确地获取和格式化时间都至关重要。Python 提供了强大的时间处理模块&#xff0c;其中 time 模块是基础且广泛使用的工具之一。本文将通过一个简单的示例&#xff…...

【javaweb项目idea版】蛋糕商城(可复用成其他商城项目)

该项目虽然是蛋糕商城项目&#xff0c;但是可以复用成其他商城项目或者购物车项目 想要源码的uu可点赞后私聊 技术栈 主要为&#xff1a;javawebservletmvcc3p0idea运行 功能模块 主要分为用户模块和后台管理员模块 具有商城购物的完整功能 基础模块 登录注册个人信息编辑…...

git gui 笔记

这里写目录标题 1. [下载安装git](https://blog.csdn.net/jiesunliu3215/article/details/111559125)2. [下载Git Gui](https://git-scm.com/downloads)3. 上传下载代码4. 创建版本5. 版本切换-checkout参考狂神说 git教程 -讲的是真的好gitee的git帮助 其他 1. 下载安装git 2…...

使用 Docker 运行 Oracle Database 23ai Free 容器镜像并配置密码与数据持久化

使用 Docker 运行 Oracle Database 23ai Free 容器镜像并配置密码与数据持久化 前言环境准备运行 Oracle Database 23ai Free 容器基本命令参数说明示例 注意事项高级配置参数说明 总结 前言 Oracle Database 23ai Free 是 Oracle 提供的免费版数据库&#xff0c;基于 Oracle …...

PyQt6医疗多模态大语言模型(MLLM)实用系统框架构建初探(下.代码部分)

医疗 MLLM 框架编程实现 本医疗 MLLM 框架结合 Python 与 PyQt6 构建,旨在实现多模态医疗数据融合分析并提供可视化界面。下面从数据预处理、模型构建与训练、可视化界面开发、模型 - 界面通信与部署这几个关键部分详细介绍编程实现。 6.1 数据预处理 在医疗 MLLM 框架中,多…...

salesforce公式字段 ISBLANK 函数和 <> NULL的区别

在 Salesforce 公式字段中&#xff0c;ISBLANK 函数和 <> NULL 的作用都可以用来检查字段是否有值&#xff0c;但它们的行为有一些显著的区别。以下是它们的详细对比和适用场景&#xff1a; 1. 基本区别 功能ISBLANK<> NULL主要作用检查字段是否为空&#xff08;适…...

微服务学习-服务调用组件 OpenFeign 实战

1. OpenFeign 接口方法编写规范 1.1. 在编写 OpenFeign 接口方法时&#xff0c;需要遵循以下规范 1.1.1.1. 接口中的方法必须使用 RequestMapping、GetMapping、PostMapping 等注解声明 HTTP 请求的类型。 1.1.1.2. 方法的参数可以使用 RequestParam、RequestHeader、PathVa…...

关于安卓greendao打包时报错问题修复

背景 项目在使用greendao的时候&#xff0c;debug安装没有问题&#xff0c;一到打包签名就报了。 环境 win10 jdk17 gradle8 项目依赖情况 博主的greendao是一个独立的module项目&#xff0c;项目目前只适配了java&#xff0c;不支持Kotlin。然后被外部集成。greendao版本…...

Ansible自动化运维实战--通过role远程部署nginx并配置(8/8)

文章目录 1、准备工作2、创建角色结构3、编写任务4、准备配置文件&#xff08;金甲模板&#xff09;5、编写变量6、编写处理程序7、编写剧本8、执行剧本Playbook9、验证-游览器访问每台主机的nginx页面 在 Ansible 中&#xff0c;使用角色&#xff08;Role&#xff09;来远程部…...

RGB 转HSV空间颜色寻找色块

文章目录 前言一、绿色确定二、红色确定总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 项目需要&#xff1a; 将RGB颜色空间转换为HSV颜色空间以寻找颜色&#xff0c;主要基于以下几个原因&#xff1a; 直观性&#xff1a; HSV颜色空间更符合人类…...

Spring Boot - 数据库集成04 - 集成Redis

Spring boot集成Redis 文章目录 Spring boot集成Redis一&#xff1a;redis基本集成1&#xff1a;RedisTemplate Jedis1.1&#xff1a;RedisTemplate1.2&#xff1a;实现案例1.2.1&#xff1a;依赖引入和属性配置1.2.2&#xff1a;redisConfig配置1.2.3&#xff1a;基础使用 2&…...

C++红黑树详解

文章目录 红黑树概念规则为什么最长路径不超过最短路径的二倍&#xff1f;红黑树的时间复杂度红黑树的结构插入叔叔节点情况的讨论只变色(叔叔存在且为红)抽象的情况变色单旋&#xff08;叔叔不存在或叔叔存在且为黑&#xff09;变色双旋&#xff08;叔叔不存在或叔叔存在且为黑…...

Qwen3-14B私有化效果:支持国密算法加密的API通信安全方案

Qwen3-14B私有化效果&#xff1a;支持国密算法加密的API通信安全方案 1. 私有部署镜像概述 Qwen3-14B私有部署镜像是基于通义千问大语言模型优化定制的专业解决方案&#xff0c;特别针对RTX 4090D 24GB显存配置进行了深度适配。这个镜像不仅提供了完整的运行环境和模型依赖&a…...

基于模拟退火算法优化的最小二乘支持向量机(SA-LSSVM)数据分类预测及Matlab代码实现...

基于模拟退火算法优化最小二乘支持向量机(SA-LSSVM)的数据分类预测 SA-LSSVM数据分类 matlab代码&#xff0c;采用交叉验证抑制过拟合问题注&#xff1a;采用交叉验证在一定程度上抑制了过拟合问题。 注&#xff1a;要求 Matlab 2018B 版本及以上最近在搞分类预测的项目&#x…...

探索声发射 b 值:Matlab 程序之旅

声发射b值&#xff0c;Matlab程序在材料科学和岩石力学等领域&#xff0c;声发射&#xff08;Acoustic Emission&#xff0c;AE&#xff09;技术是研究材料内部损伤演化的重要手段。而声发射 b 值作为其中一个关键参数&#xff0c;能反映材料内部微破裂的特征。今天&#xff0c…...

STM32环境监测系统在烟花爆竹仓库的应用

1. 项目概述与背景烟花爆竹作为一种特殊商品&#xff0c;其存储环境的安全管理一直是行业痛点。传统的人工巡检方式存在明显的滞后性——我曾亲眼见过一家小型烟花仓库因为夜间温湿度骤变而引发自燃&#xff0c;等值班人员发现时火势已难以控制。这个基于STM32的环境监测系统正…...

解决打印机标签尺寸匹配问题

在开发应用程序时,经常会遇到与打印机相关的各种问题,尤其是当需要打印特定尺寸的标签时。如果您正在开发一个可以打印产品标签的应用,并且遇到标签尺寸不匹配的问题,那么本文将为您提供详细的解决方案。 问题背景 假设您正在与同事开发一个可以打印产品标签的应用。您需…...

终极指南:如何快速免费修改艾尔登法环存档

终极指南&#xff1a;如何快速免费修改艾尔登法环存档 【免费下载链接】ER-Save-Editor Elden Ring Save Editor. Compatible with PC and Playstation saves. 项目地址: https://gitcode.com/GitHub_Trending/er/ER-Save-Editor ER-Save-Editor是一款专为《艾尔登法环》…...

LumiPixel Canvas Quest提示词反推(Interrogator)工具使用教程

LumiPixel Canvas Quest提示词反推&#xff08;Interrogator&#xff09;工具使用教程 1. 引言&#xff1a;为什么需要提示词反推工具 如果你经常使用AI绘画工具&#xff0c;一定遇到过这样的困扰&#xff1a;看到一张惊艳的作品&#xff0c;却不知道作者用了什么提示词。或者…...

FFCreator 10个实用技巧:轻松掌握视频制作的核心功能

FFCreator 10个实用技巧&#xff1a;轻松掌握视频制作的核心功能 【免费下载链接】FFCreator 一个基于node.js的高速视频制作库 A fast video processing library based on node.js 项目地址: https://gitcode.com/gh_mirrors/ff/FFCreator FFCreator是一个基于Node.js的…...

终极BewlyBewly插件指南:5分钟打造个性化Bilibili界面

终极BewlyBewly插件指南&#xff1a;5分钟打造个性化Bilibili界面 【免费下载链接】BewlyBewly Improve your Bilibili homepage by redesigning it, adding more features, and personalizing it to match your preferences. 项目地址: https://gitcode.com/gh_mirrors/be/B…...

SpringBoot+Vue实战:手把手教你搭建苍穹外卖后台管理系统(含Nginx配置避坑指南)

SpringBootVue全栈实战&#xff1a;从零构建外卖管理系统与Nginx部署精要 每次打开招聘网站&#xff0c;看到"要求有完整项目经验"的字样时&#xff0c;你是否也感到一阵心虚&#xff1f;作为全栈开发的学习者&#xff0c;我们往往陷入一个怪圈&#xff1a;学了很多碎…...