会话技术:Cookie、Session、JWT的优缺点分析与实践

ops/2024/10/18 16:55:19/

登录认证

  • 会话技术
  • 方案一:Cookie
  • 方案二:Session
  • 方案三:令牌技术
  • JWT令牌
    • 介绍
    • 生成和校验
    • 登录下发令牌案例

会话技术

  • 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应
  • 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自同一浏览器,以便在同一次会话的多个请求间共享数据
  • 会话跟踪方案:
    • Cookie(客户端会话跟踪技术)
    • Session(服务端会话跟踪技术)
    • 令牌技术

为什么要共享数据呢?
由于 HTTP 是无状态协议,在后面请求中怎么拿到前一次请求生成的数据呢?此时就需要在一次会话的多次请求之间进行数据共享。

方案一:Cookie

cookie 是客户端会话跟踪技术,它是存储在客户端浏览器的。
比如第一次请求了登录接口,登录接口执行完成之后,我们就可以设置一个 cookie,在 cookie 当中存储用户的一些数据信息如用户名、id。
服务器端在给客户端响应数据的时候,会自动的将 cookie 响应给浏览器;浏览器接收到响应回来的 cookie 之后,会自动的将 cookie 的值存储在浏览器本地。接下来在后续的每一次请求中,都会自动的将浏览器本地所存储的 cookie 携带到服务端。
基于此,服务端可以判断获取的 cookie 的值是否存在,如果存在说明客户端之前已经登录完成了。这样我们就可以基于 cookie 在同一次会话的不同请求之间来共享数据。

为什么上面都是自动的进行的?
因为 cookie 是HTTP协议当中所支持的技术,而各大浏览器厂商都支持了这一标准。在 HTTP 协议官方给我们提供了一个响应头和请求头:

  • 响应头 Set-Cookie :设置Cookie数据的
  • 请求头 Cookie:携带Cookie数据的

在这里插入图片描述
案例测试

java">@Slf4j
@RestController
public class SessionController {// 设置Cookie@GetMapping("/c1")public Result cookie1(HttpServletResponse response){response.addCookie(new Cookie("login_username", "admin"));  //设置cookie/响应cookiereturn Result.success();}// 获取cookie@GetMapping("/c2")public Result cookie2(HttpServletRequest request){Cookie[] cookies = request.getCookies();for (Cookie cookie : cookies) {if (cookie.getName().equals("login_username")){System.out.println("login_username" + cookie.getValue());  //输出获取的指定cookie}}return Result.success();}
}

Cookie的优缺点

  • 优点:HTTP协议中支持的技术(像Set-Cookie 响应头的解析以及 Cookie 请求头数据的携带,都是浏览器自动进行的,是无需我们手动操作的)
  • 缺点:
    • 移动端APP无法使用 Cookie
    • 不安全,用户可以自己禁用 Cookie
    • Cookie 不能跨域

跨域:
假如前端部署在服务器192.168.150.200,端口80上,后端部署在 192.168.150.100,端口8080上;那么我们打开浏览器访问前端工程url:http://192.168.150.200/login.html,假设服务端访问接口地址为:http://192.168.150.100:8080/login,此时就存在跨域操作了。
如果服务器设置了一个Cookie,这个Cookie是不能使用的,因为Cookie无法跨域。
区分跨域的三个维度

协议 —— ip/域名 —— 端口

只要三个有一个维度不同,就是跨域操作。

方案二:Session

Session 是服务端会话跟踪技术,它是存储在服务器端的。Session 的底层其实就是基于Cookie来实现的。
如果浏览器是第一次请求服务器,这时服务器会自动创建一个会话对象 Session(每一个会话对象 Session 都有一个 ID);服务器给浏览器响应数据时,会将 Session 的 ID 通过 Cookie 响应给浏览器,Cookie的名字是固定的 JESSIONID=ID;浏览器会自动识别这个响应头,然后将Cookie存储在浏览器本地。后续每次请求浏览器都会将 Cookie 携带到服务端。
在这里插入图片描述
案例测试

java">	// 往session中存储数据@GetMapping("/s1")public Result session1(HttpSession session){log.info("HttpSession-s1: {}", session.hashCode());session.setAttribute("loginUser", "tom");  //往session中存储数据return Result.success();}//从session中获取数据@GetMapping("/s2")public Result session2(HttpServletRequest request){HttpSession session = request.getSession();log.info("HttpSession-s2: {}", session.hashCode());Object loginUser = session.getAttribute("loginUser");  //从session中获取数据log.info("loginUser: {}", loginUser);return Result.success(loginUser);}

Session 的优缺点

  • 优点:Session是存储在服务端的,安全
  • 缺点:
    • 服务器集群环境下无法直接使用Session
    • Cookie 的缺点

服务器集群环境为何无法使用Session?
集群下同一个项目会部署多份。用户会访问一台前置的服务器,叫负载均衡服务器,再将请求均匀的分发给后面的多台服务器。因此多个请求可能分发给了不同服务器,其他服务器当中就没有这个ID的会话对象Session了。

为了解决上面这些问题,现在企业开发中基本都会采样第三种方案,通过令牌技术来进行会话跟踪。

方案三:令牌技术

令牌就是一个用户身份的标识,本质是一个字符串。
在这里插入图片描述
如果通过令牌技术来跟踪会话,在第一次请求(如登录)成功的时候,我们就可以生成一个令牌,,令牌就是用户的合法身份凭证;接下来在响应数据时,直接将令牌响应给前端;前端接收到令牌后,需要将这个令牌存储起来,可以存储在 cookie 中,也可存储在其他的存储空间(如 localStorage)当中。后续每次请求都需要将令牌携带到服务端,服务端检验令牌有效性。
令牌 的优缺点

  • 优点:
    • 支持 PC端、移动端
    • 解决集群环境下的认证问题
    • 减轻服务器的存储压力(无需在服务器端存储)
  • 缺点:
    • 需要自己实现(包括令牌的生成、令牌的传递、令牌的校验)

JWT令牌

介绍

JWT 全称:JSON Web Token(官网:https://jwt.io/)

  • 定义了一种简洁的、自包含的格式,用于在通信双方以 json 数据格式安全的传输信息。由于数字签名的存在,这种信息是可靠的。

    简洁:是指 jwt 就是一个简单的字符串。
    自包含:指 jwt 令牌看似是一个随机的字符串,但是我们可以根据自身的需求在 jwt 令牌中存储自定义的数据内容(如用户的相关信息)。
    简单来讲,jwt就是将原始的json数据格式进行了安全的封装,这样就可以直接基于jwt在通信双方安全的进行信息传输了。

JWT的组成:

  • Header(头):记录令牌类型、签名算法等。 例如:{“alg”:“HS256”,“type”:“JWT”}
  • Payload(有效载荷):携带一些自定义信息、默认信息等。 例如:{“id”:“1”,“username”:“Tom”}
  • Signature(签名):防止Token被篡改、确保安全性。结合header、payload,并加入指定秘钥,通过指定签名算法计算而来。
    正是因为签名的存在,所以整个 jwt 令牌是非常安全可靠的。
    在这里插入图片描述

JWT 如何将原始的 json 格式数据转为字符串的呢?
进行 Base64编码:一种基于64个可打印的字符(A-Z a-z 0-9 + /)来表示二进制数据的编码方式。此外还有等号用于补位。
签名部分是通过指定的签名算法计算出来的。


JWT令牌最典型的应用场景就是登录认证:
  1. 在浏览器发起请求来访问登录的接口,如果登录成功之后,我们需要生成一个jwt令牌,将生成的 jwt令牌返回给前端。
  2. 前端拿到jwt令牌之后,会将jwt令牌存储起来。在后续的每一次请求中都会将jwt令牌携带到服务端。
  3. 服务端统一拦截请求之后,先来判断一下这次请求有没有把令牌带过来,如果没有带过来,直接拒绝访问,如果带过来了,还要校验一下令牌是否是有效。如果有效,就直接放行进行请求的处理。

生成和校验

需要先引入 JWT 的依赖:

<!-- JWT依赖-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

生成JWT令牌的代码实现:

java">@Test
public void testGenerateJwt(){HashMap<String, Object> claims = new HashMap<>();claims.put("id", 1);claims.put("name", "tom");// 构建 JWT 令牌String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, "itcitel")  //签名算法, 密钥.setClaims(claims)  //自定义内容(载荷).setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))  //设置有效期为1h.compact();//将JWT转换为紧凑的字符串表示:以Base64编码压缩头部和载荷,并在之后添加签名System.out.println(jwt);
}

在这里插入图片描述
下面校验生成的JWT令牌(解析):

java">@Test
public void testParseJwt(){Claims claims = Jwts.parser().setSigningKey("itcitel")  //签名密钥(必须和生成令牌时使用的相同).parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTcwNjUwMDQ5NH0.Bs_W_knSJ9hCFL-lwEB_kXlYr3rewgskF62AMJAA37k").getBody();System.out.println(claims);
}

在这里插入图片描述

令牌解析后,可以看到id和过期时间,如果在解析的过程当中没有报错,就说明解析成功了。

登录下发令牌案例

java">@Slf4j
@RestController
@CrossOrigin
public class LoginController {@Autowiredprivate UserService userService;@PostMapping("/login")public Result login(@RequestBody User user){log.info("用户登录:{}", user);User u = userService.login(user);// 如果登录成功,生成令牌,下发令牌if (u != null){Map<String, Object> claims = new HashMap<>();claims.put("id", u.getId());claims.put("name", u.getUsername());String jwt = JwtUtils.generateJwt(claims);return Result.success(jwt);}return Result.error("用户名或密码错误");}
}

http://www.ppmy.cn/ops/99136.html

相关文章

【数据结构4】树的实例-模拟文件系统、二叉树的遍历(先序遍历、中序遍历、后序遍历、层次遍历)

1 树和二叉树 2 树的实例-模拟文件系统 3 二叉树 3.1 二叉树的遍历 二叉树的先序遍历 二叉树的中序遍历 二叉树的后序遍历 二叉树的层次遍历 1 树 树是一种数据结构 比如:目录结构 树是一种可以递归定义的数据结构树是由n个节点组成的集合:如果n0&#xff0c;那这是一棵空树;如…

STM32MP157_uboot_简介

STM32MP157_uboot_简介 前言&#xff1b; uboot 的全称是 Universal Boot Loader&#xff0c;uboot 是一个遵循 GPL 协议的开源软件&#xff0c;uboot 是一个裸机代码&#xff0c;可以看作是一个裸机综合例程。现在的 uboot 已经支持液晶屏、网络、USB 等高级功能。uboot 官网为…

ARM程序的组成和执行过程

ARM程序的组成&#xff1a; 此处所说的“ARM程序”是指在ARM系统中正在执行的程序&#xff0c;而非保存在ROM中的bin映像&#xff08;image&#xff09;文件&#xff0c;这一点清注意区别。一个ARM程序包含3部分&#xff1a;RO&#xff0c;RW和ZI RO是程序中的指令和常量 RW是程…

edge浏览器可以,chrome浏览器看不到接口数据

chrome 谷歌浏览器&#xff0c;可以看到页面&#xff0c;F12的开发者工具看不到返回数据 无法加载响应数据: No data found for resource with given identifier Chrome 将显示 ERR_INTERNET_DISCONNECTED 错误 edge浏览器是正常的。 哈哈哈哈哈哈&#xff0c;这里误点了&a…

通过Golang实现中间人攻击,查看和修改https流量包

要查看和修改 HTTPS 流量包&#xff0c;需要使用一个能够执行 中间人攻击&#xff08;Man-in-the-Middle, MITM&#xff09; 的代理工具。这个工具将拦截并解密 HTTPS 流量&#xff0c;然后允许查看和修改流量包的内容&#xff0c;再将其重新加密并发送到目标服务器。 完整的 …

【一起学Rust | 框架篇 | Tauri2.0框架】tauri中rust和前端的相互调用(rust调用前端)

文章目录 前言1. rust中调用前端2. 如何向前端发送事件3. 前端监听事件4. 执行js代码 前言 近期Tauri 2.0 rc版本发布&#xff0c;2.0版本迎来第一个稳定版本&#xff0c;同时官方文档也进行了更新。Tauri是一个使用Rust构建的框架&#xff0c;可以让你使用前端技术来构建桌面…

linux和docker部署基本的命令掌握

git用到的指令 上传代码 git add . git commit -m zhushi git push 拉取代码 git clone 代码仓地址 git pulldocker用到的指令 # 查看docker下的容器进程,停止和删除 docker ps -a docker stop name(id) docker rm name(id) # docker下面的镜像和删除 docker images docker r…

前端面试题整理-webpack

实现前端模块化&#xff0c;将多个 js&#xff0c;打包成一个 bundle.js (其他类型文件交由各自的 loader 处理) 1. webpack 了解吗&#xff1f;大概介绍一下 一种打包工具&#xff0c;实现前端模块化&#xff0c;将多个 js&#xff0c;打包成一个 bundle.js (其他类型文件交…