Web 身份认证 --- Session和JWT Token
Web 身份认证 --- Session和JWT Token
- 方法一: 通过使用Session进行身份认证
- 方法二: 通过JWT token进行身份认证
- 什么是JWT
- JWT完整流程
- JWT攻防
- JWT 如何退出登录
- JWT的续签
方法一: 通过使用Session进行身份认证
- 用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建创建对应的 Session
- 请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器,浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名.
- 当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作.
- 当流量大时,后端往往需要多台服务器共同来支撑前端用户请求,那如果用户在 A 服务器登录了,第二次请求跑到服务 B 就会出现登录失效问题, 此时可以使用共享session
- 将用户的 Session 等信息使用缓存中间件来统一管理 (如Redis),保障分发到每一个服务器的响应结果都一致
使用SpringBoot实现简单的session + cookie实现持久登录
- 当用户第一次login时
@PostMapping("login")public JsonData login(@RequestBody User user, HttpServletRequest request, HttpServletResponse response) {String crypPassword = MD5Utils.MD5(user.getPwd());User loggedinUser = userService.login(user.getPhone(), crypPassword);if (loggedinUser != null) {//随机生成一个UUID作为seesion IDString sessionID = UUID.randomUUID().toString();//在服务器中将sessionID和用户组成 KV pair: Map<sessionID, USER>//setAttribute的底层实现是一个hashmaprequest.getSession().setAttribute(sessionID, loggedinUser);//生成一个cookie, 并将sessionID加入cookie, 对应的名字是 "sessionId"Cookie cookie = new Cookie("sessionId", sessionID);cookie.setPath("/");cookie.setMaxAge(FIVE_DAYS);//在response中给客户端返回cookieresponse.addCookie(cookie);return JsonData.buildSuccess(loggedinUser);}return JsonData.buildError("Password or username invalid");}
- 在interceptor中判断是否已经登录:
public static int currentUserID = -1;String sessionId = "Default";
Cookie[] cookies = request.getCookies();
if (cookies == null) {System.out.println("Intercepter: No Cookies");sendJsonMessage(response, JsonData.buildError("Interceptor: No Cookies, please log in first"));currentUserID = -1;return false;
}//遍历前端传来的cookie
for (Cookie cookie: cookies) {//如果发现 sessionId字段, 则说明发现sessionIDif (cookie.getName().equals("sessionId")) {System.out.println("Interceptor: found a session id in cookie:" + cookie.getValue());//拿到sessionIdsessionId = cookie.getValue();break;}
}
//通过sessionID拿到user
User user = (User)request.getSession().getAttribute(sessionId);if (sessionId.equals("Default") || user == null) {System.out.println("Interceptor: sessionId not match, cannot preceed");sendJsonMessage(response, JsonData.buildError("Interceptor: No record of this sessionID, please log in first"));currentUserID = -1;return false;
}//拿到userID, 在别的类中可以通过 int uid = LoginInterceptor.currentUserID; 拿到uid
currentUserID = user.getId();
- Session模式有个明显缺点就是在分布式系统中,session缓存可能被频繁调用,形成性能瓶颈甚至发生单点故障,导致系统崩溃.
- 可以使用JWT进行验证,JWT是无状态的验证方法,不需要缓存
方法二: 通过JWT token进行身份认证
什么是JWT
- JWT,全称为JSON Web Token,是一种开放标准(RFC 7519),用于在网络应用环境间安全地传输信息。
- 它本质上是一个经过数字签名的JSON对象,能够携带并传递状态信息(如用户身份验证、授权等)
- JWT由 Header + Payload + Signature三部分组成, 并经过Base64Url编码之后使用 (注意编码不是加密)
Header
- JWT 的头部通常由两部分组成,分别是令牌类型(typ)和加密算法(alg)。一般情况下,头部会采用 Base64 编码。
{"alg": "HS256","typ": "JWT"
}
Payload
- JWT 的载荷也称为声明信息,包含了一些有关实体(通常是用户)的信息以及其他元数据。通常包括以下几种声明:
- Registered Claims:这些声明是预定义的,包括 iss(发行者)、sub(主题)、aud(受众)、exp(过期时间)、nbf(生效时间)、iat(发布时间)和 jti(JWT ID)等。
- Public Claims:这些声明可以自定义,但需要注意避免与注册声明的名称冲突。
- Private Claims:这些声明是保留给特定的应用程序使用的,不会与其他应用程序冲突。
- 注意:Payload中一定不要存放敏感或重要信息,如密码等
{"sub": "666666","name": "warriors","admin": true
}
Signature
- JWT 的签名是由头部、载荷和密钥共同生成的。它用于验证 JWT 的真实性和完整性。一般情况下,签名也会采用 Base64 编码。
例如,如果要使用 HMAC SHA256 算法,将按以下方式创建签名:
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
JWT完整流程
- 假设你现在有一个应用,需要用户登录后获取访问权限。
用户登录
- 用户在登录页面输入用户名和密码,点击登录。服务器接收到登录请求并验证用户的凭证。
生成JWT
- 如果用户的凭证(账号密码)正确,服务器将生成一个JWT。假如服务器将生成的JWT为:
- eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- 让我们详细分解这个JWT的生成过程,一共三个部分,每个部分被点号(.)截断
Header: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
- base64url解码一下得到
{"alg": "HS256","typ": "JWT"
}
- 这个头部表示我们将使用HMAC SHA-256算法进行签名,并且这是一个JWT。
Payload: eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
- base64url解码一下得到
{"sub": "1234567890","name": "John Doe","iat": 1516239022
}
- 这个负载部分包含了一些声明,比如用户ID(sub)、用户名(name)和签发时间。
Signature: SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- 第三部分签名的生成是由第一部分和第二部分组成的
- 我们使用密钥(secret)来生成签名。签名是使用以下方法生成的:
- 将编码后的Header和Payload用点号(.)连接在一起:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
- 使用HMAC SHA-256算法和密钥secret对上述字符串进行签名:
header_payload = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ"
secret = "secret"
signature = hmac.new(secret.encode(), header_payload.encode(), hashlib.sha256).digest()
signature_base64 = base64.urlsafe_b64encode(signature).rstrip(b'=').decode()
print(signature_base64)
- 生成的第三部分签名是:
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- 最后,将编码后的Header、Payload和生成的Signature用点号(.)连接在一起,形成完整的JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
返回JWT
- 服务器将生成的JWT返回给客户端。客户端可以将JWT存储在本地存储(Local Storage)或Cookie中。
携带JWT进行请求
- 在用户登录后的每次请求中,客户端会在请求头中携带JWT,例如:
- Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
服务器验证JWT
- 服务器接收到请求后,会验证JWT的签名是否正确。验证过程如下:
- 从JWT中提取出header和payload部分。
- 使用同样的签名密钥和header中的算法,重新生成签名。
- 将重新生成的签名与JWT中的签名进行比较,如果相同,则验证通过。
响应请求
- 如果JWT有效且未过期,服务器处理请求并返回响应。
JWT攻防
常见攻击方法
- 暴力破解签名密钥:攻击者可以使用暴力破解的方法猜测JWT的签名密钥。如果签名密钥比较弱,这种攻击可能会成功。
- 算法替换攻击:如果服务器在验证JWT时不严格检查头部的alg字段,攻击者可以将alg字段改为none,绕过签名验证。
- 伪造Token:如果服务器使用对称加密算法(如HS256),并且密钥泄露,攻击者可以使用密钥伪造有效的JWT
防御措施
- 使用强密钥:确保签名密钥足够强,难以被暴力破解。
- 严格验证算法:在服务器端严格验证alg字段,拒绝none算法。
- 使用非对称加密:使用非对称加密算法(如RS256),即使公钥泄露,攻击者也无法伪造JWT。
- 定期更换密钥:定期更换签名密钥以增加安全性。
- 设置适当的过期时间:设置合理的JWT过期时间,减少被盗用的风险。
JWT 如何退出登录
JWT一旦发出,不能撤回,所以在有效期内会一直有效,常见退出方法有三种
- Simply remove the token from the client
- Create a token blacklist
- Just keep token expiry times short and rotate them often
JWT的续签
- JWT在使用中一般遵循OAuth2.0的标准,OAuth2.0中的续签通常使用“双令牌(access_token, refresh_token) 续签”
- 注意 access_token和refresh_token是OAuth2规范的内容,不是jwt规范的部分
访问令牌(Access Token)
- 用途:访问令牌是一个短期的令牌,用于授权第三方应用访问受保护的资源。每次访问受保护的资源时,第三方应用需要在请求头中携带这个令牌。
- 有效期:访问令牌通常有一个较短的有效期,可能是几分钟到几小时不等。这是为了减少令牌被盗用的风险。
- 格式:访问令牌通常是一个字符串,可以是 JWT(JSON Web Token)格式,也可以是其它格式。
刷新令牌(Refresh Token)
- 用途:刷新令牌用于获取新的访问令牌。当访问令牌过期时,客户端可以使用刷新令牌向授权服务器请求新的访问令牌,而不需要再次通过用户的授权。
- 有效期:刷新令牌的有效期通常比访问令牌长得多,可以是几天、几周甚至更长时间。
- 安全性:由于刷新令牌的有效期较长,应该妥善保护,避免泄露。一般来说,刷新令牌不应该在前端应用中存储,而应存储在服务器端
为什么需要两个token
首先确认一点: token使用的次数越多被窃取概率越大,过期时间越长也就越危险
- 一个token的续签方法: 假设现在只有一个access token,因为使用次数很多(每次Api调用都会用到)并且token无法被设置为失效, 所以access token的过期时间需要被设置的很短,这样即使被token盗,损失也可以降到最低.
- 方法一: token过期之后,用户重新输入账号密码拿到新的access token.
- 缺点:因为access token过期时间很短,用户需要频繁输入账号密码,很影响使用.
- 方法二:当服务器收到请求之后,计算token剩余有效时间,如果小于一个阈值,则颁发新access token.
- 缺点:如果用户在阈值之内没有进行api调用,则还是需要重新输入账号和密码,影响使用
- 缺点:如果access被盗用,则盗用者还是可以通过被盗用的access token进行续签,拿到新的token.
使用两个token续签:
- OAuth2定义了
资源服务器
和授权服务器
,第一次认证向授权服务器提交用户名和密码获得两个token,然后访问资源服务器获取资源使用access_token- 当access_token有效期失效的时候,前端使用这个refresh_token请求授权服务器换取新的acceess_token来保证接口的正常请求, 这样可以做到用户无感知续签
- 这样即使access token被盗用,access token的过期时间设置的很短,损失也可以降到最低.
- 而refresh token使用次数少,保存在数据库中,被盗风险小
相关文章:

Web 身份认证 --- Session和JWT Token
Web 身份认证 --- Session和JWT Token 方法一: 通过使用Session进行身份认证方法二: 通过JWT token进行身份认证什么是JWTJWT完整流程JWT攻防JWT 如何退出登录JWT的续签 方法一: 通过使用Session进行身份认证 用户第一次请求服务器的时候,服务器根据用户提交的相关信…...

UE5制作倒计时功能
设置画布和文本 文本绑定 格式化时间 转到事件图表,计算时间,时间结束后面的事件可以按自己需求写 进入关卡蓝图,添加倒计时UI...

Linux去除注释和空行
平时查看某些配置文件的时,我们会发现有很多注释(如:"#"开头的行),中间还有很多空行,看起来非常费劲,所以在这里总结下如何去除注释和空行的方法。 举例说明 这里选个简单点的文件&a…...

Elasticsearch 7.x入门学习-Spring Data Elasticsearch框架
1 Spring Data框架 Spring Data 是一个用于简化数据库、非关系型数据库、索引库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷,并支持 map-reduce 框架和云计算数据服务。 Spring Data 可以极大的简化 JPA的写法,…...

网络层IP协议(TCP)
IP协议: 在了解IP协议之前,我们市面上看到的"路由器"其实就是工作在网络层。如下图: 那么网络层中的IP协议究竟是如何发送数据包的呢? IP报头: IP协议的报头是比较复杂的,作为程序猿只需要我们重…...

计算机视觉中的边缘检测算法
摘要: 本文全面深入地探讨了计算机视觉中的边缘检测算法。首先阐述了边缘检测的重要性及其在计算机视觉领域的基础地位,随后详细介绍了经典的边缘检测算法,包括基于梯度的 Sobel 算子算法、Canny 边缘检测算法等,深入剖析了它们的…...

js 常用扩展方法总结+应用
文章目录 js 常用扩展方法总结扩展方法应用选择大型项目 中扩展方法应用选择小型项目中 扩展应用 js 常用扩展方法总结 函数原型(prototype)扩展方法 介绍:在JavaScript中,通过修改函数的prototype属性可以为该函数创建的所有对象…...

数据结构---图(Graph)
图(Graph)是一种非常灵活且强大的数据结构,用于表示实体之间的复杂关系。在图结构中,数据由一组节点(或称为顶点)和连接这些节点的边组成。图可以用于表示社交网络、交通网络、网络路由等场景。 1. 基本概…...

前端解析超图的iserver xml
前端解析超图的iserver xml const res await axios.get(url)const xmlDom new DOMParser().parseFromString(res.data, text/xml);// 获取versionconst version xmlDom.getElementsByTagNameNS(*, ServiceTypeVersion)[0].textContent// 获取layerconst layerDom xmlDom.ge…...

LocalForage 使用指南:统一管理 LocalStorage、WebSQL 和 IndexedDB
前言 在前端开发中,客户端数据存储是一个至关重要的环节。无论是用户偏好设置、缓存内容,还是表单数据,都需要一个高效、可靠的存储方案。浏览器原生提供的 LocalStorage、SessionStorage 和 IndexedDB 等 API 虽然功能强大,但使…...

代码随想录算法训练营第五天-哈希-242.有效的字母异位词
这道题的总体感觉不是很难,但是其完成的思想还是很有趣的利用数据下标来代表字母序列然后遍历两个字符串每个字符,给对应字母下标的数组中一个自增,另一个自减通过查看最后的数组内容是不是0,来判断是不是异位词 #include <io…...

学习maven(maven 项目模块化,继承,聚合)
前言 本篇博客的核心:理解maven 项目模块化,继承,聚合 的含义 maven 项目模块化 含义 maven项目模块化:使用maven 构建项目,管理项目的方式,我们可以将maven项目根据内在的关系拆分成很多个小项目【模块】…...

KDD 2025预讲会:10位一作的论文分享与话题思辨|12月18日全天直播
点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! 圆桌思辨:一作们的KDD 2025投稿经验分享与热点探讨 1. KDD 2025 与往年相比有哪些新变化?两次投稿周期的新规则有哪些影响? 2. 第一篇KDD的工作是如何成功被接收的࿱…...

掌握特征提取:机器学习中的 PCA、t-SNE 和 LDA模型
文章目录 一、说明二、既然有 PCA 技术降维,为什么还要学习 t-SNE?2.1 t-SNE的核心思想:2.2 保持点之间的局部关系有什么意义?2.3 t-SNE 的几何直觉: 三、t-SNE 的数学公式:四、目标函数:五、梯…...

JAVA基础:注释
JAVA基础:注释 作用 使得代码中的一段文本不被执行,起到解释说明的作用。 分类 JAVA中的注释有三种: 单行注释 //单行注释多行注释 /* 多 行 注 释 */文档注释 /***@deprecated comments* @author lhy*/文档注释可以添加一些参数作为说明。 有趣的代码注释 卡车/* * *…...

从源码构建安装Landoop kafka-connect-ui
背景 部署Landoop kafka-connect-ui最简单的办法还是通过docker来部署,我们之前的kafka-connect-ui就是通过docker部署的,但是,最近发现个问题:当使用docker部署且防火墙使用的是firewalld的情况下,就会出现端口冲突。…...

【自动驾驶】Ubuntu22.04源码安装Autoware Core/Universe
【自动驾驶】Ubuntu22.04源码安装Autoware Core/Universe 官方源码安装教程前置条件安装ROS2 Humble安装Autoware Core/Universe配置开发环境配置工作空间设置控制台 官方源码安装教程 链接:https://autowarefoundation.github.io/autoware-documentation/main/ins…...

使用Nexus3搭建npm私有仓库
一、npm介绍 npm的全称是Node Package Manager,它是一个开放源代码的命令行工具,用于安装、更新和管理Node.js模块。npm是Node.js的官方模块管理器,它允许用户从一个集中的仓库中下载和安装公共的Node.js模块,并将这些模块集成到…...

OpenHarmony和OpenVela的技术创新以及两者对比
两款有名的国内开源操作系统,OpenHarmony,OpenVela都非常的优秀。本文对二者的创新进行一个简要的介绍和对比。 一、OpenHarmony OpenHarmony具有诸多有特点的技术突破和重要贡献,以下是一些主要方面: 架构设计创新 分层架构…...

【LeetCode每日一题】Leetcode 1071.字符串的最大公因子
Leetcode 1071.字符串的最大公因子 题目描述: 对于字符串 s 和 t,只有在 s t t t … t t(t 自身连接 1 次或多次)时,我们才认定 t 能除尽 s。 给定两个字符串 str1 和 str2 。返回 最长字符串 x,要…...

《C++:计算机视觉图像识别与目标检测算法优化的利器》
在当今科技飞速发展的时代,计算机视觉领域正经历着前所未有的变革与突破。图像识别和目标检测作为其中的核心技术,广泛应用于安防监控、自动驾驶、智能医疗等众多领域,其重要性不言而喻。而 C语言,凭借其卓越的性能、高效的资源控…...

大模型的构建与部署(2)——数据清洗
版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl1. 数据清洗的必要性与影响 1.1 数据清洗对模型性能的影响 数据清洗是数据预处理的关键步骤,对于模型训练的性能和准确性有着直接的影响。原始数据中的缺失值、重复值、异常值以及数据格式不一致…...

试题转excel;word转excel;大风车excel
一、问题描述 一名教师朋友,偶尔会需要整理一些高质量的题目到excel中 以往都是手动复制搬运,几百道题几乎需要一个下午的时间 关键这些事,枯燥无聊费眼睛,实在是看起来就很蠢的工作 就想着做一个工具,可以自动处理…...

微信小程序webview和小程序通讯
1.背景介绍 1.1需要在小程序嵌入vr页面,同时在vr页面添加操作按钮与小程序进行通信交互 1.2开发工具:uniapp开发小程序 1.3原型图 功能:.点击体验官带看跳转小程序的体验官带看页面 功能:点击立即咨询唤起小程序弹窗打电话 2.…...

ChatGPT大模型 创作高质量文案的使用教程和案例
引言 随着人工智能技术的飞速发展,大语言模型如 ChatGPT 在创作文案、生成内容方面展现出了强大的能力。无论是个人用户还是企业用户,都可以利用 ChatGPT 提高工作效率、激发创意、甚至解决实际问题。本文将详细介绍 ChatGPT 如何帮助创作各类高质量文案,并通过具体案例展示…...

Vue Web开发(八)
1. VueWeb面包屑和tag的布局 本章节完成VueWeb面包屑和tag的布局,并且与左侧菜单联系,涉及组件间通信。 1.1. 页面创建 (1)首先我们先完成每个页面的路由,之前已经有home页面和user页面,缺少mail页面和其…...

element-ui实现table表格的嵌套(table表格嵌套)功能实现
最近在做电商类型的官网,希望实现的布局如下:有表头和表身,所以我首先想到的就是table表格组件。 表格组件中常见的就是:标题和内容一一对应: 像效果图中的效果,只用基础的表格布局是不行的,因…...

【考前预习】4.计算机网络—网络层
往期推荐 【考前预习】3.计算机网络—数据链路层-CSDN博客 【考前预习】2.计算机网络—物理层-CSDN博客 【考前预习】1.计算机网络概述-CSDN博客 目录 1.网络层概述 2.网络层提供的两种服务 3.分类编址的IPV4 4.无分类编址的IPV4—CIDR 5.IPV4地址应用规划 5.1使用定长子…...

【java】MDC
目录 1. 说明2. 作用3. 使用4. 与TraceID的关系5. 注意事项 1. 说明 1.MDC(Mapped Diagnostic Context)是一个用于在多线程环境中追踪和传递日志上下文信息的机制。2.映射诊断环境。3.MDC是一个线程本地的、可维护的、可传递的上下文环境。4.它允许开发…...

Android 好的开源库
1. 权限请求框架 GitHub - getActivity/XXPermissions: Android 权限请求框架,已适配 Android 14 2. 下载框架 GitHub - lingochamp/okdownload: A Reliable, Flexible, Fast and Powerful download engine....