JWT验证

news2024/11/17 4:52:43

JSON Web Token 入门教程 - 阮一峰的网络日志 (ruanyifeng.com)

img

  • 补充上时间线?画图?
  • 隐患是什么
  • 为什么一开始不这么做
  • 这个封面挺好做的,以后笔记我也做一个,,要是能自动生成就好了

一、认证

  • 为了保存信息用的,除了认证信息还有些其他信息

cookie:在前端直接用cookie保存

session:用cookie在前端保存session_id,在后端用session_id保存内容

session在内存中,另外,cookies被获取的话,可能会跨站请求伪造攻击

  • 获取token会怎么样?不会跨站?
  • cookie网络传输时是明文吗?
  • 和cookie相比,是在服务器端验证了下 token是否存在么,,纯cookie模式就是cookie发啥我都信
    • 怎么验证?
    • 放在Authorization可以防止XSS和XSRF?

检查

  • 签名是否有效
  • 是否过期
  • 接受方是否是自己

这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。

举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?

一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。

  • 单点失败?

另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。

JWT简介

原理

JWT 的原理是,服务器认证以后,生成一个字符串,发回给用户,就像下面这样。

img

它是一个很长的字符串,没有换行,中间用点(.)分隔成三个部分。

  • 怎么是json对象,方便前端使用么?话说这是认证用的,还指望用里面的信息?
  • 后端会再转换成json对象么?还是说一开始是个json对象,一开始这个怎么来的,为什么是少见的后端先生成JSON,因为后端先拿到数据么

以后,用户与服务端通信的时候,都要发回这个字符串。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。

服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

  • 无状态概念!

JWT 的三个部分依次如下

Header.Payload.Signature

元数据被定义为:描述数据的数据,对数据及信息资源的描述性信息。

Header

Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。

{
  "alg": "HS256",
  "typ": "JWT"
}

上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT

最后,将上面的 JSON 对象使用 Base64URL 算法(详见后文)转成字符串。

payload 负载

是一个JSON 对象, 用来存放实际需要传递的数据,形如:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

其中payload官方规定了7个字段:

  • iss (issuer):签发人

  • exp (expiration time):过期时间

  • sub (subject):主题

  • aud (audience):受众

  • nbf (Not Before):生效时间

  • iat (Issued At):签发时间

  • jti (JWT ID):编号

除官方字段外,也可以直接使用私有字段(就像js对象的成员一样,毕竟是json数据),如之前例子中的admin

注意,JWT 默认是不加密的,不要把机密信息放在这个部分。Base64URL虽然人类难以看懂,但不是加密doge

  • 可逆么?不可逆是不是和md5类似

Signature

Signature 部分是对前两部分的签名,防止数据篡改。

签名的生成:

需要指定一个密钥(secret,一般用随机盐),这个密钥只有服务器才知道。使用 Header 里面指定的签名算法,按照下面的公式产生签名:

HMACSHA256(
   base64UrlEncode(header) + "." +
   base64UrlEncode(payload),
   secret
)

这里以HMACSHA256加密算法为例

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

  • 为什么又把header和payload编码再加密?不能直接加密?只能从字符串加密?
  • 怎么解密?能解密的话还要前两部分干什么?

3.4 Base64URL

前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+/=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-/替换成_ 。这就是 Base64URL 算法。

Base64可以被解码,没有加密

JWT 的使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

Authorization: Bearer <token>

另一种做法是,放在 POST 请求的数据体里面。

cookie是和域名有关的,所以不能跨域

  • 安全机制把,原理是什么?后端人看见跨域cookie直接不用?
  • 自动发送?

JWT 的几个特点

(1)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。

(2)JWT 不加密的情况下,不能将秘密数据写入 JWT。

(3)JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

(4)JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

(5)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

(6)为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

  • 自包含:自己包含用户需要的信息,不需要查询数据库

验签:

服务端拿到jwt后,将其中的header和payload取出来,和自己服务端保存的secret放一起计算一次signature,如果计算出来的signature和之前保存的signature一样,则说明数据没有收到更改

如果中间人改了三部分中其中一项,将导致验签时计算出来的signature和之前不一样,无法认证。如果中间人修改了signature,直接就不用算就能知道被修改了。(发来的签名和现场计算的签名要和保存的签名一样。jwt认证用的,携带的信息不会在前端更改)

这就是为什么signature要header.payload.secret一起加密。我们需要header、payload中的信息,所以只是编码;而用于认证的加密则要三项信息都没有被修改。

  • 这是防修改的方式,和计网有点儿像?

  • 如果被人拿到了token假装身份,解决方式是超时淘汰?

  • 是不是安全要考虑防修改、防伪装、防查看?这个是传递的死数据,不超时就不会主动修改,那会主动修改的知道有没有被中间人修改?

  • 防查看这里倒是直接不写认证信息,,,要是cookie的话,不会要每次把密码发上去查查数据库把,,或者登录一次获取用户名cookie,然后谁拿着这个cookie就一直用?

  • 密码保存到cookie岂不是很麻烦?就算加密也只能对称吧,浏览器给他加个盐?

需求分析

前后端通信方式

  • 不能用cookie,跨域
  • 放在header的Authorization中
    • 需要传递编码后的字符串数据,能有+、换行吗?
      • 所以用base64url

目的:

  • 不敏感的用户信息能读取
    • 所以不能更改
    • 所以用base64url,可以还原信息
  • 认证的secret要加密,密钥不能让人读取
    • 要加密
    • 要保证所有数据不能被中间人更改,因为后端还要读取这些信息
      • 要一起加密

总结需求:

  • 数据要有合适的解析格式

  • 数据要有合适的传输格式,要用能跨域、防止攻击的传输方式

  • 要能从jwt读取用户非敏感信息,

    • 因此这部分数据不能被篡改
  • 不能从jwt读取密钥,所以传输时要加密

为什么不直接传输json、对json加密?为了方便么?都先json数据类型 为什么要用base64?因为数据类型、特殊符号吗?还是说加密算法没法直接加密json和这些特殊符号,所以原始数据要做处理?类似于加一层思想,屏蔽了特殊性?

但是处理的时候json更方便,虽然java创建时传参是<String, Objecr>map,

  • 这种map对应json?json是不是全字符串?还是说js数据类型?

话说java不用json包的话是不是经常用map代替,,都是键值对甚么区别,,

JWT验证对象

JWTVerifier,指定密钥和创建jwt时一样

常见异常

按触发顺序:

  • 算法不匹配异常
  • 签名不匹配异常
  • 过期异常
  • payload异常(payload数据不对劲儿,可能被修改)

可以看出是先验签再看数据

  • https不会被拦截?

springboot使用JWT

封装成工具类

这里是ruoyi-cloud中的jwt工具类,省略了导包

import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.TokenConstants;
import com.ruoyi.common.core.text.Convert;
import io.jsonwebtoken.Jwts;


public class JwtUtils
{
    public static String secret = TokenConstants.SECRET;

    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    public static String createToken(Map<String, Object> claims)
    {
        String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
        return token;
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    public static Claims parseToken(String token)
    {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }

    /**
     * 根据令牌获取用户标识
     * 
     * @param token 令牌
     * @return 用户ID
     */
    public static String getUserKey(String token)
    {
        Claims claims = parseToken(token);
        return getValue(claims, SecurityConstants.USER_KEY);
    }

    /**
     * 根据令牌获取用户标识
     * 
     * @param claims 身份信息
     * @return 用户ID
     */
    public static String getUserKey(Claims claims)
    {
        return getValue(claims, SecurityConstants.USER_KEY);
    }

    /**
     * 根据令牌获取用户ID
     * 
     * @param token 令牌
     * @return 用户ID
     */
    public static String getUserId(String token)
    {
        Claims claims = parseToken(token);
        return getValue(claims, SecurityConstants.DETAILS_USER_ID);
    }

    /**
     * 根据身份信息获取用户ID
     * 
     * @param claims 身份信息
     * @return 用户ID
     */
    public static String getUserId(Claims claims)
    {
        return getValue(claims, SecurityConstants.DETAILS_USER_ID);
    }

    /**
     * 根据令牌获取用户名
     * 
     * @param token 令牌
     * @return 用户名
     */
    public static String getUserName(String token)
    {
        Claims claims = parseToken(token);
        return getValue(claims, SecurityConstants.DETAILS_USERNAME);
    }

    /**
     * 根据身份信息获取用户名
     * 
     * @param claims 身份信息
     * @return 用户名
     */
    public static String getUserName(Claims claims)
    {
        return getValue(claims, SecurityConstants.DETAILS_USERNAME);
    }

    /**
     * 根据身份信息获取键值
     * 
     * @param claims 身份信息
     * @param key 键
     * @return 值
     */
    public static String getValue(Claims claims, String key)
    {
        return Convert.toStr(claims.get(key), "");
    }
}

重点总结:

  • 密钥作为静态变量,导入

有用auth0包下的jwt的,也是工厂模式,方法名格式是create和withxxx

验证是给框架或者拦截器

JWT标准里面定义的标准claim包括:

  • iss(Issuser):JWT的签发主体;
  • sub(Subject):JWT的所有者;
  • aud(Audience):JWT的接收对象;
  • exp(Expiration time):JWT的过期时间;
  • nbf(Not Before):JWT的生效开始时间;
  • iat(Issued at):JWT的签发时间;
  • jti(JWT ID):是JWT的唯一标识。

token过期的续期方案

单token方案

图片

  • 将 token 过期时间设置为15分钟;
  • 前端发起请求,后端验证 token 是否过期;如果过期,前端发起刷新token请求,后端为前端返回一个新的token;
  • 前端用新的token发起请求,请求成功;
  • 如果要实现每隔72小时,必须重新登录,后端需要记录每次用户的登录时间;用户每次请求时,检查用户最后一次登录日期,如超过72小时,则拒绝刷新token的请求,请求失败,跳转到登录页面。

另外后端还可以记录刷新token的次数,比如最多刷新50次,如果达到50次,则不再允许刷新,需要用户重新授权。

在若依中,似乎没见前端调用refresh接口,,后端要是小于过期时间了就刷新(redis重新设置);

token要是失效了,直接抛异常(redis里查不到token就是失效,不用比较时间);

但是刷新token的比较时间却是直接获取当前登录用户,然后在java内存比较,,比起去redis找会快一些么,,这个用户放不放redis似乎关系不大了,,

双token方案
  • 登录成功以后,后端返回 access_tokenrefresh_token,客户端缓存此两种token;
  • 使用 access_token 请求接口资源,成功则调用成功;如果token超时,客户端携带 refresh_token 调用token刷新接口获取新的 access_token;
  • 后端接受刷新token的请求后,检查 refresh_token 是否过期。如果过期,拒绝刷新,客户端收到该状态后,跳转到登录页;如果未过期,生成新的 access_token 返回给客户端。
  • 客户端携带新的 access_token 重新调用上面的资源接口。
  • 客户端退出登录或修改密码后,注销旧的token,使 access_tokenrefresh_token 失效,同时清空客户端的 access_tokenrefresh_toke

微信网页授权是通过OAuth2.0机制实现的,也使用了双token方案。

微信网页授权方案
  • 用户在第三方应用的网页上完成微信授权以后,第三方应用可以获得 code(授权码)。code的超时时间为10分钟,一个code只能成功换取一次access_token即失效。
  • 第三方应用通过code获取网页授权凭证access_token和刷新凭证 refresh_token。
  • access_token是调用授权关系接口的调用凭证,由于access_token有效期(2个小时)较短,当access_token超时后,可以使用refresh_token进行刷新。
  • refresh_token拥有较长的有效期(30天),当refresh_token失效的后,需要用户重新授权。

后端实现token过期还可以利用Redis来存储token,设置redis的键值对的过期时间。如果发现redis中不存在token的记录,说明token已经过期了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/636769.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【深度学习】日常笔记3

如果分类问题具有预测这样带有自然顺序的问题&#xff0c;如{婴⼉, ⼉童, ⻘少年, ⻘年⼈, 中年⼈, ⽼年⼈}&#xff0c;那么可以把分类问题转变为回归问题了。不过可以使用独热编码one-hot encoding。 类别对应的分量设置为1&#xff0c;其他所有分量设置为0。在我们的例⼦中…

Cadence原理图快速查找元器件的方法

1.Cadence原理图快速查找元器件的方法 ①在红框中输入元器件编号&#xff0c;点击望远镜的图标在底下的状态栏可看到查找到的相关元器件&#xff0c;点击元器件可自动定位当前元器件的位置。 ②点击hierarchy&#xff08;层&#xff09;可自主查找&#xff0c;找到后点击序号即…

【项目实战】一、Spring boot整合JWT、Vue案例展示用户鉴权

前言 案例整合了Spring boot、Spring Cloud alibaba、Gateway、Nacos discovery、Nacos config、openFeign、JWT、Vue3、Router、Axios等&#xff1b;通过JWT和登录、查询&#xff08;带用户信息&#xff09;接口&#xff0c;验证了上述工具以及鉴权功能。 1、若无公共模块&a…

学好Java爬虫需要什么技巧

Java爬虫是一种利用Java编程语言编写的网络爬虫程序&#xff0c;它可以自动化地浏览和抓取互联网上的数据&#xff0c;并将数据进行处理和保存。Java爬虫通常使用HTTP协议模拟浏览器请求来获取网页内容&#xff0c;并通过解析HTML网页标签和属性等信息来提取有用的数据。Java爬…

PPT处理控件Aspose.Slides入门教程:在 C# 中加密和解密 PPT

Aspose API支持流行文件格式处理&#xff0c;控件覆盖 word、excel、PDF、条码、OCR、CAD、HTML、email、ppt、等各个文档管理领域 是一款 PowerPoint管理API&#xff0c;用于读取&#xff0c;编写&#xff0c;操作和转换PowerPoint幻灯片的独立API&#xff0c;可将PowerPoint…

【网页设计】第 2 课 - 网页设计规范

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、网页规范 3、设计规范 4、banner 简介 4.1、Banner 的定义 4.2、Banner 的类型 4.3、Banner 构图 4.4、…

chatgpt赋能python:Python学习笔记:如何合并元组

Python学习笔记&#xff1a;如何合并元组 在Python中&#xff0c;元组是一种不可变的数据结构。当我们需要组合不同的元组时&#xff0c;我们可以使用元组合并的方法来实现。在本文中&#xff0c;我们将学习如何使用Python语言来合并元组。 什么是元组 在Python语言中&#…

windows 服务程序和桌面程序集成(六)集成安装、启动、卸载功能

系列文章目录链接&#xff1a; windows 服务程序和桌面程序集成&#xff08;一&#xff09;概念介绍windows 服务程序和桌面程序集成&#xff08;二&#xff09;服务程序windows 服务程序和桌面程序集成&#xff08;三&#xff09;UDP监控工具windows 服务程序和桌面程序集成&…

AntDesign——TableAPI学习

table表格用于展示数据 https://ant.design/components/table-cn#table 1.bordered false不显示每一个小表格的边框&#xff0c;true反之 2.columns 列名及列数据&#xff0c;接受columns数组 2.1 colums中必须声明的属性 title&#xff08;列标题&#xff09; dataInde…

为什么会被扣小红书品牌违规分,原因是什么

小红书在2022年经过一次较大点的规则变动&#xff0c;其中小红书品牌违规分就是其中亮点名词之一。很多人对此都不甚了解&#xff0c;今天为大家分享下为什么会被扣小红书品牌违规分&#xff0c;原因是什么&#xff1f; 一、什么是品牌违规分 品牌违规分是小红书在2022年4月20日…

工厂模式~

核心本质 ① 实例化对象不使用new&#xff0c;用工厂方法代替 ② 将选择实现类&#xff0c;创建对象统一管理和控制&#xff0c;从而将调用者跟我们的实现类解耦 简单工厂 public interface Car {void name(); }public class Tesla implements Car{Overridepublic void name()…

基于 opencv 的人脸识别上课考勤系统,附源码,可作为毕业设计

一、简介 这个人脸识别考勤签到系统是基于大佬的人脸识别陌生人报警系统二次开发的。 项目使用Python实现&#xff0c;基于OpenCV框架进行人脸识别和摄像头硬件调用&#xff0c;同时也用OpenCV工具包处理图片。交互界面使用pyqt5实现。 该系统实现了从学生信息输入、人脸数据…

ps复制图层警告 (不能从选区建立新图层,因为所选区域是空的。)解决方法

有时我们选完选区 按 CtrlJ 复制图层 会出现这种情况 问题出在你当前选的图层 因为 我选择的这块选区在第二个图层上 但很明显 选择的是一大个图层 简单说 你操作的选区必须在你当前选择的图层上才行 也就是 我现在要将选择区换成第二个图层才行 再按 CtrlJ 图层就出来了

AssetStudio工程导入VS各种报错解决

AssetStudio下载地址&#xff1a;https://github.com/Perfare/AssetStudio 工程导入&#xff0c;生成解决方案&#xff0c;然后报了一堆错。让我们来一个一个的解决 这个错误&#xff0c;是缺少System.Runtime.InteropServices.RuntimeInformation.dll文件&#xff0c;下载并添…

“爱心助考 为梦护航”雷锋志愿者在行动

为确保我市高考、学考工作顺利进行&#xff0c;为考生营造安全温馨的考试环境保驾护航&#xff0c;共青团怀化市委、市教育局、共青团鹤城区委、区教育局联合怀化市青少年关爱协会党支部&#xff0c;开展2023“爱心助考 为梦护航”雷锋志愿者服务活动。 6月7-9日高考三天&#…

开发新项目看过来,这3款基于 Vue 的免费开源的 admin 管理后台框架非常好用

三款 admin 框架&#xff0c;分别基于热门的前端 UI 组件库 ElementPlus / Ant Design / Naive UI 打造&#xff0c;开箱即用。 新项目的开始&#xff0c;一般是搭建 admin 系统&#xff0c;今天盘点一下3个好的选择。 Vue vben admin 了解详细&#xff1a;https://www.thos…

C型标准气动阀线圈插头安装距8mm

8mm针脚距气动阀插头、C型DIN标准电气插头。这些插头通常用于工业自动化、机械控制等领域。 工业标准&#xff0c;C型&#xff0c;DIN43650 / EN175301-803,插针中心距 8mm、3针脚、4针脚&#xff0c;额定电压 250V,工作电流 6A,最大接线 0.75mm2,电缆锁紧口 PG7,电缆外径 4-6…

Beyond Compare 4 无法打开

解决办法&#xff1a; 1.修改注册表。WINR呼出开始菜单&#xff0c;在搜索栏中输入 regedit&#xff0c;点击确定。 2.删除项目&#xff1a;\HKEY_CURRENT_USER\Software\ScooterSoftware\Beyond Compare 4\CacheId 根据这个路径找到cacheid 右击删除掉就可以

Allegro Design Entry CIS导出原理图BOM方法

1.Allegro Design Entry CIS导出原理图BOM方法 Tools->Bill of Materials 填入项分别为&#xff1a; Header:项次\t名称\t位号\t值\t封装\t数量 Combined property string: {Item}\t{PartName}\t{Reference}\t{Value}\t{PCB footprint}\t{Quantity} 点击OK生成如下表格

服务架构的进化之路:探索服务架构的演进之路

1、引言 服务架构是一种以服务为中心的软件设计模式&#xff0c;将应用程序拆分为一组小而自治的服务单元。随着互联网和信息技术的快速发展&#xff0c;软件系统变得越来越复杂。为了应对这种变化&#xff0c;服务架构也在不断地演变和发展。本文将简要介绍服务架构的发展史&…