JWT身份验证

JWT知识点

jwt,全称 json web token, JSON Web 令牌是一种开放的行业标准 RFC 7519 方法,用于在两方之间安全地表示声明。

详情可以参考: hhttps://jwt.io/introduction

  1. 数据结构

JSON Web Token由三部分组成,它们之间用圆点.进行分割, 一个标准的JWT形如 xxx.yyy.zzz

●Header

●Payload

●Signature

1.1 header

即第一部分,由两部分组成:token的类型(JWT)和算法名称(比如:HMAC SHA256或者RSA等等)。

一个具体实例如

java 复制代码
{
  "alg": "HS256",
  "typ": "JWT"
}

然后,用Base64对这个JSON编码就得到JWT的第一部分

1.2 Payload

第二部分具体的实体,可以写入自定义的数据信息,有三种类型

●Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss (issuer 签发者), exp (expiration time 有效期), sub (subject), aud (audience)等。

●Public claims : 可以随意定义。

●Private claims : 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明

如一个具体实例

java 复制代码
{
    "iss": "一灰灰blog",
    "exp": 1692256049,
    "wechat": "https://spring.hhui.top/spring-blog/imgs/info/wx.jpg",
    "site": "https://spring.hhui.top",
    "uname": "一灰"
}

对payload进行Base64编码就得到JWT的第二部分

1.3 Signature

为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥,签名算法是header中指定的那个,然对它们签名即可。

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

签名是用于验证消息在传递过程中有没有被更改,并且,对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方。

1.4 具体实例

下面给出一个基于 java-jwt 生成的具体实例

java 复制代码
public static void main(String[] args) {
    String token = JWT.create().withIssuer("一灰灰blog").withExpiresAt(new Date(System.currentTimeMillis() + 86400_000))
            .withPayload(MapUtils.create("uname", "一灰", "wechat", "https://spring.hhui.top/spring-blog/imgs/info/wx.jpg", "site", "https://spring.hhui.top"))
            .sign(Algorithm.HMAC256("helloWorld"));
    System.out.println(token);
}

技术派的应用姿势

  1. 通用的jwt鉴权方案

JWT鉴权流程

一个简单的基于jwt的身份验证方案如下图

基本流程分三步:

1 用户登录成功之后,后端将生成的jwt返回给前端,然后前端将其保存在本地缓存;

2 之后前端与后端的交互时,都将jwt放在请求头中,比如可以将其放在Http的身份认证的请求头Authorization,也可以通过自定义的请求头来传递

3 后端接收到用户的请求,从请求头中获取jwt,然后进行校验,通过之后,才响应相关的接口;否则表示未登录

说明:技术派沿用session的方案,依然将jwt写入到cookie中

  1. jwt时使用姿势

接下来看一下再技术派中的实际使用场景,核心逻辑再com.github.paicoding.forum.service.user.service.help.UserSessionHelper

我们直接再之前session的基础上进行优化,这里主要借助开源项目java-jwt来实现JWT的生成管理

生成JWT的实现

我们定义了签发者、有效期,指定了签名算法,然后实体类中,携带两个信息

●s: 即之前生成的sessionId,我们借助自定义的traceId生成工具来生成唯一的会话id

●u: 用户userId

上面的实现中,有几个通用的成员属性,我们通过自定义的配置,再项目启动时进行初始化,避免后续的重复创建

自定义的jwt三个配置属性

java 复制代码
paicoding:
  jwt:
    issuer: pai_coding # 签发者
    secret: hello_world # 签名密钥
    expire: 2592000000 # jwt的有效期,默认30天

jwt校验

用户每次请求时,依然是沿用之前的session方式的校验逻辑,再Filter层,从请求头中,获取Cookie,找到对应的jwt,然后尝试根据jwt获取对应的用户信息

注意上面的实现,即便jwt校验通过了,我们也依然从redis中去查了一下,判断是否有效

为什么这么设计呢?

因为jwt本身无状态,后端完全可以将redis这一层的存储都直接干掉,纯依赖jwt的特性来完成身份鉴权,但是由于它的无状态,后端减少存储压力是一个好处,同样也是一个弊端,后端失去了token的管控权限,如果我们希望提前失效某些用户身份,则无法支持

鉴于此,我们依然保留了redis中存储jwt的方案(这是主要原因,当然技术派作为一个让大家获取更多的知识点教学相长的项目,我们会尽可能的将相关知识点给引入进来,且尽量满足大厂项目规范来实现)

  1. 实例体验

接下来我们实际体验一下,线上运行技术派的jwt,登录之后,找到一个请求,找一下cookie中的f-session

我们解析一下看下是啥

站在用户的角度,说实话你用session还是jwt,没有什么实质的感受差异,那么为什么还会有这两种不同的技术方向呢?

session与jwt的对比

|------------------------------|--------------------------|
| jwt | session |
| 前端存储,通用的校验规则,后端再获取jwt时校验是否有效 | 前端存索引,后端判断session是否有效 |
| 验签,不可篡改 | 无签名保障,安全性由后端保障 |
| 可存储非敏感信息,如用户名,头像等 | 一般不存储业务信息 |
| jwt生成时,指定了有效期,本身不支持续期以及提前失效 | 后端控制有效期,可提前失效或者自动续期 |
| 通常以请求头方式传递 | 通常以cookie方式传递 |
| 可预发csrf攻击 | session-cookie方式存在csrf风险 |

关于上面的安全风险,给一个简单的扩展说明

相关推荐
智慧老师23 分钟前
Spring基础分析13-Spring Security框架
java·后端·spring
lxyzcm24 分钟前
C++23新特性解析:[[assume]]属性
java·c++·spring boot·c++23
V+zmm101341 小时前
基于微信小程序的乡村政务服务系统springboot+论文源码调试讲解
java·微信小程序·小程序·毕业设计·ssm
Oneforlove_twoforjob1 小时前
【Java基础面试题025】什么是Java的Integer缓存池?
java·开发语言·缓存
xmh-sxh-13141 小时前
常用的缓存技术都有哪些
java
AiFlutter2 小时前
Flutter-底部分享弹窗(showModalBottomSheet)
java·前端·flutter
J不A秃V头A2 小时前
IntelliJ IDEA中设置激活的profile
java·intellij-idea
DARLING Zero two♡2 小时前
【优选算法】Pointer-Slice:双指针的算法切片(下)
java·数据结构·c++·算法·leetcode
小池先生3 小时前
springboot启动不了 因一个spring-boot-starter-web底下的tomcat-embed-core依赖丢失
java·spring boot·后端
CodeClimb3 小时前
【华为OD-E卷-木板 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od