Session 和 JWT(JSON Web Token)都是用于在用户和服务器之间建立认证状态的机制,但它们在工作原理、存储方式和安全性等方面存在着一些差异,下面我们一起来看。

1.什么是JWT?

Session 我们已经很熟悉了,那什么是 JWT 呢?

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络上安全传输信息的简洁、自包含的方式。它通常被用于身份验证和授权机制。

JWT 由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。

  1. 头部(Header):包含了关于生成该 JWT 的信息以及所使用的算法类型。
  2. 载荷(Payload):包含了要传递的数据,例如身份信息和其他附属数据。JWT 官方规定了 7 个字段,可供使用:
    1. iss (Issuer):签发者。
    2. sub (Subject):主题。
    3. aud (Audience):接收者。
    4. exp (Expiration time):过期时间。
    5. nbf (Not Before):生效时间。
    6. iat (Issued At):签发时间。
    7. jti (JWT ID):编号。
  3. 签名(Signature):使用密钥对头部和载荷进行签名,以验证其完整性。

JWT 官网:https://jwt.io/

2.JWT优点分析

JWT 相较于传统的基于会话(Session)的认证机制,具有以下优势:

  1. 无需服务器存储状态:传统的基于会话的认证机制需要服务器在会话中存储用户的状态信息,包括用户的登录状态、权限等。而使用 JWT,服务器无需存储任何会话状态信息,所有的认证和授权信息都包含在 JWT 中,使得系统可以更容易地进行水平扩展。
  2. 跨域支持:由于 JWT 包含了完整的认证和授权信息,因此可以轻松地在多个域之间进行传递和使用,实现跨域授权。
  3. 适应微服务架构:在微服务架构中,很多服务是独立部署并且可以横向扩展的,这就需要保证认证和授权的无状态性。使用 JWT 可以满足这种需求,每次请求携带 JWT 即可实现认证和授权。
  4. 自包含:JWT 包含了认证和授权信息,以及其他自定义的声明,这些信息都被编码在 JWT 中,在服务端解码后使用。JWT 的自包含性减少了对服务端资源的依赖,并提供了统一的安全机制。
  5. 扩展性:JWT 可以被扩展和定制,可以按照需求添加自定义的声明和数据,灵活性更高。

总结来说,使用 JWT 相较于传统的基于会话的认证机制,可以减少服务器存储开销和管理复杂性,实现跨域支持和水平扩展,并且更适应无状态和微服务架构。

3.JWT基本使用

在 Java 开发中,可以借助 JWT 工具类来方便的操作 JWT,例如 HuTool 框架中的 JWTUtil。

HuTool 介绍:https://doc.hutool.cn/pages/JWTUtil/

使用 HuTool 操作 JWT 的步骤如下:

  1. 添加 HuTool 框架依赖
  2. 生成 Token
  3. 验证和解析 Token

3.1 添加 HuTool 框架依赖

在 pom.xml 中添加以下信息:

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>

3.2 生成 Token

Map<String, Object> map = new HashMap<String, Object>() {
private static final long serialVersionUID = 1L;
{
put("uid", Integer.parseInt("123")); // 用户ID
put("expire_time", System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 15); // 过期时间15天
}
};
JWTUtil.createToken(map, "服务器端秘钥".getBytes());

3.3 验证和解析 Token

验证 Token 的示例代码如下:

String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2MjQwMDQ4MjIsInVzZXJJZCI6MSwiYXV0aG9yaXRpZXMiOlsiUk9MRV_op5LoibLkuozlj7ciLCJzeXNfbWVudV8xIiwiUk9MRV_op5LoibLkuIDlj7ciLCJzeXNfbWVudV8yIl0sImp0aSI6ImQ0YzVlYjgwLTA5ZTctNGU0ZC1hZTg3LTVkNGI5M2FhNmFiNiIsImNsaWVudF9pZCI6ImhhbmR5LXNob3AifQ.aixF1eKlAKS_k3ynFnStE7-IRGiD5YaqznvK2xEjBew";
JWTUtil.verify(token, "123456".getBytes());

解析 Token 的示例代码如下:

String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9.U2aQkC2THYV9L0fTN-yBBI7gmo5xhmvMhATtu8v0zEA";
final JWT jwt = JWTUtil.parseToken(rightToken);
jwt.getHeader(JWTHeader.TYPE);
jwt.getPayload("sub");

3.4 代码实战

在登录成功之后,生成 Token 的示例代码如下:

// 登录成功,使用 JWT 生成 Token
Map<String, Object> payload = new HashMap<String, Object>() {
private static final long serialVersionUID = 1L;
{
put("uid", userinfo.getUid());
put("manager", userinfo.getManager());
// JWT 过期时间为 15 天
put("exp", System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 15);
}
};
String token = JWTUtil.createToken(payload, AppVariable.JWT_KEY.getBytes());

例如在 Spring Cloud Gateway 网关中验证 Token 的实现代码如下:

import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.example.common.AppVariable;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import java.util.List; /**
* 登录过滤器(登录判断)
*/
@Component
public class AuthFilter implements GlobalFilter, Ordered {
// 排除登录验证的 URL 地址
private String[] skipAuthUrls = {"/user/add", "/user/login"}; @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 当前请求的 URL
String url = exchange.getRequest().getURI().getPath();
for (String item : skipAuthUrls) {
if (item.equals(url)) {
// 继续往下走
return chain.filter(exchange);
}
}
ServerHttpResponse response = exchange.getResponse();
// 登录判断
List<String> tokens =
exchange.getRequest().getHeaders().get(AppVariable.TOKEN_KEY);
if (tokens == null || tokens.size() == 0) {
// 当前未登录
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// token 有值
String token = tokens.get(0);
// JWT 效验 token 是否有效
boolean result = false;
try {
result = JWTUtil.verify(token, AppVariable.JWT_KEY.getBytes());
} catch (Exception e) {
result = false;
}
if (!result) {
// 无效 token
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
} else { // 判断 token 是否过期
final JWT jwt = JWTUtil.parseToken(token);
// 得到过期时间
Object expObj = jwt.getPayload("exp");
if (expObj == null) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
long exp = Long.parseLong(expObj.toString());
if (System.currentTimeMillis() > exp) {
// token 过期
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
}
return chain.filter(exchange);
} @Override
public int getOrder() {
// 值越小越早执行
return 1;
}
}

4.JWT实现原理

JWT 本质是将秘钥存放在服务器端,并通过某种加密手段进行加密和验证的机制。加密签名=某加密算法(header+payload+服务器端私钥),因为服务端私钥别人不能获取,所以 JWT 能保证自身其安全性。

5.Session VS JWT

Session 和 JWT 的区别主要有以下几点:

  1. 工作原理不同:Session 机制依赖于服务器端的存储。当用户首次登录时,服务器会创建一个会话,并生成一个唯一的会话 ID,然后将这个 ID 返回给客户端(通常是通过Cookie)。客户端在后续的请求中会携带这个会话 ID,服务器根据会话ID来识别用户并获取其会话信息;而 JWT 是一种无状态的认证机制,它通过在客户端存储令牌(Token)来实现认证。当用户登录时,服务器会生成一个包含用户信息和有效期的 JWT,并将其返回给客户端。客户端在后续的请求中会携带这个 JWT,服务器通过验证 JWT 的有效性来识别用户。
  2. 存储方式不同:Session 信息存储在服务器端,通常是保存在内存或数据库中。这种方式需要服务器维护会话状态,因此在分布式系统或微服务架构中,会话信息的共享和同步可能会成为问题;而 JWT信息存储在客户端,通常是保存在浏览器的本地存储或 HTTP 请求的头部中。这种方式无需服务器维护会话状态,使得 JWT 在分布式系统或微服务架构中更加灵活和易于扩展。
  3. 有效期和灵活性不同:Session 的有效期通常由服务器控制,并且在会话期间用户状态可以在服务器端动态改变。但这也意味着服务器需要管理会话的生命周期;而 JWT 的有效期可以在令牌生成时设置,并且可以在客户端进行缓存和重复使用。这使得 JWT 在需要频繁访问资源且不需要频繁更改用户状态的场景中更加适用。此外,JWT 还支持在令牌中包含自定义的用户信息,提供了更大的灵活性。

课后思考

既然 JWT 的有效期是在令牌生成时设置的,那如何实现 JWT 的自动续期呢?又如何将已经泄漏的 JWT 令牌作废呢?

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

面试官:Session和JWT有什么区别?的更多相关文章

  1. 当面试官问你GET和POST区别的时候,请这么回答.......

    文章内容转载于微信公众号WebTechGarden 一.GET和POST的'普通'区别 GET和POST是HTTP请求的两种基本方法,要说它们的区别,接触过WEB开发的人都能说出一二. 最直观的区别就 ...

  2. 【对线面试官】CountDownLatch和CyclicBarrier的区别

    <对线面试官>系列目前已经连载31篇啦,这是一个讲人话面试系列 [对线面试官]Java注解 [对线面试官]Java泛型 [对线面试官] Java NIO [对线面试官]Java反射 &am ...

  3. 在面试官问你BS和CS区别的时候如何回答??

    这是我下来整理好的,如果哪里不全,望大家多多指教 C/S是Client/Server的缩写.服务器通常采用高性能的PC.工作站或小型机,并采用大型数据库系统,如Oracle.Sybase.Inform ...

  4. 接口测试彻底弄懂Session、Cookie、Token的区别及联系hold住面试官--hold住了开3万,hold不住开3K!

    一.前言:接口测试之伤:cookie,session,token本是一家! cookie,session,token的区别早就已经成为测试同行的心病,各大论坛,各大博客,各大视频网站无不充斥着各种疑问 ...

  5. 看完这篇 Session、Cookie、Token,和面试官扯皮就没问题了

    Cookie 和 Session HTTP 协议是一种无状态协议,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录:Session 和 Cookie 的主要目的 ...

  6. 如何用json 与jsonp 的区别去回答你的面试官?

    常常 有面试官这样问我们,虽然用过无数次,但是回答不上岂不是尴尬,那我们浅析一下它们的区别? 1. json JSON是一种基于文本的数据交换格式,用于描述复杂的数据,举个例子: var nax=[ ...

  7. 面试官:你说你懂i++跟++i的区别,那你会做下面这道题吗?

    面试官:你说你懂i++跟++i的区别,那你知道下面这段代码的运行结果吗? 面试官:"说一说i++跟++i的区别" 我:"i++是先把i的值拿出来使用,然后再对i+1,++ ...

  8. Get与Post的区别?(面试官最想听到的答案)

    GET和POST是HTTP请求的两种基本方法,要说它们的区别,接触过WEB开发的人都能说出一二. 最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数. 你可能自己 ...

  9. 面试官:小伙子,你给我说一下Java Exception 和 Error 的区别吧?

    前言 昨天在整理粉丝给我私信的时候,发现了一个挺有意思的事情.是这样的,有一个粉丝朋友私信问我Java 的 Exception 和 Error 有什么区别呢?说他在面试的时候被问到这个问题卡壳了,最后 ...

  10. [每日一题]面试官问:for in和for of 的区别和原理?

    关注「松宝写代码」,精选好文,每日一题 ​时间永远是自己的 每分每秒也都是为自己的将来铺垫和增值 作者:saucxs | songEagle 一.前言 2020.12.23 日刚立的 flag,每日一 ...

随机推荐

  1. 硬件开发笔记(七): 硬件开发基本流程,制作一个USB转RS232的模块(六):创建0603封装并关联原理图元器件

    前言   有了原理图,可以设计硬件PCB,在设计PCB之间还有一个协同优先动作,就是映射封装,原理图库的元器件我们是自己设计的.为了更好的表述封装设计过程,本文描述了贴片电阻电容0603芯片封装,创建 ...

  2. 案例分享:某品牌音响系列协议调试工具(搜寻主机,查询通道,基本控制API,云音乐API,语言节目API等,可增删改指令)

    需求   某音响品牌需要一套完整的协议调试工具,提供给研发人员,渠道商,客户,现场人员等使用:  1.使用sqlite3数据库存储协议,  2.搜寻主机,操作主机:  3.探测云端API,调试API: ...

  3. 【LeetCode二叉树#07】左叶子节点之和(基于栈的迭代法前中后序遍历复习)

    左叶子节点之和 力扣题目链接(opens new window) 计算给定二叉树的所有左叶子之和. 示例: 思路 注意审题,这里是要求 左叶子节点 之和 不是二叉树中的左侧节点之和,因此使用层序遍历是 ...

  4. ThreadLocal原理解析

    ThreadLocal源码分析 /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * ...

  5. 【Azure 环境】使用az login登录遇见OSError: [WinError -2146893813] : '' 错误

    问题描述 使用 az login指令登录,遇见 OS Error: [WinError -2146893813] : '', 在指令中添加 --debug后,输出的错误消息为: urllib3.con ...

  6. Springboot 撞上 NebulaGraph——NGbatis 初体验

    本文首发于 NebulaGraph 公众号 https://mp.weixin.qq.com/s/z56o6AEz1Z4RmS8Zdx6dTA 大家好,我是开源项目 NGbatis 的发起人大叶(Co ...

  7. Codeforces Round 169 (Div. 2)C. Little Girl and Maximum Sum(差分、贪心)

    目录 题面 链接 题意 题解 代码 总结 题面 链接 C. Little Girl and Maximum Sum 题意 给q个[l,r]将所有这些区间里面的数相加和最大. 可以进行的操作是任意排列数 ...

  8. 使用PdfSharp从模板生成Pdf文件

    ​ 最近在做一个生成文档的需求.通过先制作一个包含各字段占位符的文档模板,导入这个模板并填写内容替换掉占位符,再输出成最终文件. 由于版式固定,安全性更好,业务上常用Pdf作为最终标准化的格式, 在. ...

  9. [VueJsDev] 快速入门 - vue项目根目录配置文件

    [VueJsDev] 目录列表 https://www.cnblogs.com/pengchenggang/p/17037320.html vue项目根目录配置文件 ::: details 目录 目录 ...

  10. getUserMedia 获取 摄像头 拍照 代码 1920-1080 video canvas

    getUserMedia 获取 摄像头 拍照 代码 1920-1080 video canvas 20210422 摄像头代码 是基于 https 协议的,需要开启协议后才能测试 http-serve ...