剖析cookie,session,token,jwt的区别与联系

一 Cookie

1.什么是cookie?

HTTP cookie也叫cookie,最初用于在客户端存储会话信息。这个规范要求: 服务器通过发送Set-Cookie HTTP头部包含会话信息(服务器创建),例如下面是包含头部的一个HTTP响应:

yaml 复制代码
	HTTP/1.1 200 OK
	Content-type: text/html
	Set-Cookie: name=value  //HTTP响应会设置一个名为"name",值为"value"的cookie
	Other-header: other-header-value
浏览器会保存这些会话信息(浏览器保存),并在之后每个请求中都会通过HTTP头部再将cookie发给服务器,比如:
	GET /index.jsl HTTP/1.1
	Cookie: name=value
	Other-header: other-header-value
cookie是与特定域绑定的。设置cookie后,他会与请求一起发送到创建他的域。这个限制能保证**cookie中存储的信息只对被认可的接收者开放,不能被其他域访问**。

2.cookie使用注意事项

因为所有cookie都会作为请求头部发送给服务器,所以cookie太大可能会影响客户端对特定域的请求性能。保存的cookie越大,请求完成的时间就越长。对于大量的数据cookie并非最佳存储方式,于是出现了Web Storage。

二 Session

1.广义上的Session技术

HTTP是无状态的,为了能够在HTTP协议上保持住状态,比如用户是否登陆接需要一种方案来把用户的一个个无状态HTTP请求关联起来。这种技术就叫Session。Session的功能就是个一个个分离的HTTP请求关联起来,只要实现这个功能,基本上本能叫Session的一种实现。

  • 在Cookie里放个JSESSIONID,在服务器上保持状态,用户请求来了,根据这个JSESESSIONID去服务器里查状态。这是Tomcat的实现方法。
  • 把所有状态都存在Cookie里,服务器给个签名防止伪造,每次请求来了,直接充Cookie里面获取状态,这是JWT的实现方法。
  • 在Cookie里放个token,状态不存在中间件里,而是存在Redis里,这也是一种Session实现方法。

把Sessin存储在Web中间件中(比如Tomcat),这种做法正在淘汰,因为这种方案对负载均衡不友好,也不利于快速伸缩。 把Session存在Redis和前端的才是最佳方案,尤其在微服务架构大行其道的情况下。 只要HTTP还是无状态的,只要保存状态的是刚需,Session就不会消失,变化的只是它的实现方式。

2.传统的Session的代码和原理分析

java 复制代码
package com.pug.zixun.controller;


@RestController
@Slf4j
@Api(tags = "用户管理")
public class PassportLoginController extends BaseController {

    @PostMapping("/login")
    @ApiOperation("登录信息")
    public String saveUser(@RequestBody UserVo userVo, HttpSession session) {
        // 账号和密码正确
        //将用户信息存入session中
        session.setAttribute("loginuser", userVo.getUsername());
        return "登录成功";
    }

    @GetMapping("/info")
    @ApiOperation("登录信息")
    public String getuserinfo(HttpSession session) {
        // 账号和密码正确
        //从session中取出当前登录的用户信息
        return "当前登录是是: " + session.getAttribute("loginuser");
    }
}

3.传统session存在的问题

  • session存储在内存中。是由tomcat来管理和运行的。
  • 内存的问题:重启就会丢失。tomcat重启以后session就丢失
  • session的存储数据在tomcat内存,如果你网站用户很大,很容易把jvm的内存撑满。当用户量很大的,很容易内存溢出
  • 前后端分离时由于前端与后端往往不在同一个域中,而cookie跟后端的域绑定才生效因此产生跨域问题;
  • 部署多个tomcat服务时由于session只存在某个tomcat中,其余的tomcat则无法获取session。

4.spring-session实现session共享[将session统一存储在redis中]

具体实现 依赖

pom.xml 复制代码
//引入redis
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
//引入连接池
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
//引入session-redis
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

配置

application.yml 复制代码
//spring-session的配置
spring:
  session:
    store-type: redis
    timeout: 3600
    redis:
      namespace: login_user
      
  //redis的配置
  redis:
    host: 127.0.0.1
    port: 6379
    database: 7
    password: xxxxxxx
    lettuce:
      pool:
        max-active: 20
        max-wait: -1
        max-idle: 5
        min-idle: 0

注解

java 复制代码
package com.pug.zixun.controller;

@RestController
@Slf4j
@Api(tags = "用户管理")
public class PassportLoginController extends BaseController {

    @PostMapping("/login")
    @ApiOperation("登录信息")
    public String saveUser(@RequestBody UserVo userVo, HttpSession session) {
        // 账号和密码正确
        session.setAttribute("loginuser", userVo.getUsername());
        return "登录成功";
    }
    @GetMapping("/info")
    @ApiOperation("登录信息")
    public String getuserinfo(HttpSession session) {
        // 账号和密码正确
        return "当前登录是是: " + session.getAttribute("loginuser");
    }
}

数据存入redis

三 Token

1.为什么要使用token

你可能会说spring-session不香吗。为什么还要去自定义呢?原因很多时候我们的项目是前后端分离的项目开发方式。很多的端比如APP、小程序根本就没有cookie的概念。所以我们必须自己去实现。这样自由度也非常的高。 代码

java 复制代码
package com.zl.springbootssm.controller.session;

import com.zl.springbootssm.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * Description:
 * Author: Mr.Zhao
 * Create Date Time: 2023/11/22 21:29.
 * Update Date Time:
 */
@RestController
@Slf4j
@RequestMapping("/token")
public class LoginTokenController {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @PostMapping ("/login")
    public String saveUser(@RequestBody User user) {
        log.info(String.valueOf(user));
        String key = "token" + UUID.randomUUID().toString();
//        假设账号和密码正确
        stringRedisTemplate.opsForValue().set(key, user.getUserName(), 3600, TimeUnit.SECONDS);
        return key;
    }

    @GetMapping("/info")
    public String getinfo(String token) {
        return "当前登录时" + stringRedisTemplate.opsForValue().get(token);
    }
}

2.存在问题

使用token虽然解决了自定义cookie的问题但仍然需要使用redis对session进行存储。

四 Jwt

1.jwt与token的区别

token是一串毫无意义的随机数,而jwt可以存储用户信息。 JWT是json web token缩写。它将用户信息加密到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证JWTToken的正确性,只要正确即通过验证。

2.整合jwt的具体步骤

1.依赖

pom.xml 复制代码
<!--        引入jwt-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.19.2</version>
        </dependency>

2.配置 无 3.注解

LoginJwtController.java 复制代码
package com.zl.springbootssm.controller.session;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.zl.springbootssm.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.AlgorithmConstraints;
import java.util.Date;


@RestController
@Slf4j
@RequestMapping("/jwt")
public class LoginJwtController {
    //    秘钥
    public final static String key = "admin123";

    @GetMapping("login")
    public String login(User user) {
        Algorithm algorithm = Algorithm.HMAC256(key);
        String jwt = JWT.create()
                .withIssuer("zhaoliang")
                .withClaim("loginuser", user.getUserName())
                .withExpiresAt(new Date(System.currentTimeMillis() + 20000))
                .sign(algorithm);
        return jwt;
    }
    @GetMapping("/info")
    public DecodedJWT getinfo(String token) {
        Algorithm algorithm = Algorithm.HMAC256(key);
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("zhaoliang")
                .build();
        DecodedJWT jwt = verifier.verify(token);
        return jwt;
    }

}
相关推荐
喵个咪20 分钟前
基于 Go-Kratos 与 MCP 的推荐服务实战指南
后端·深度学习·微服务
ZHOUZAIHUI2 小时前
WSL(Ubuntu24.04) 安装PostgreSQL
开发语言·后端·scala
i02082 小时前
SpringBoot 项目配置
java·spring boot·后端
月屯3 小时前
后端go完成文档分享链接功能
开发语言·后端·golang
Franciz小测测3 小时前
Python连接RabbitMQ三大方案全解析
开发语言·后端·ruby
海梨花3 小时前
又是秒杀又是高并发,你的接口真的扛得住吗?
java·后端·jmeter
Livingbody3 小时前
win11上wsl本地安装版本ubuntu25.10
后端
用户8356290780514 小时前
如何在 C# 中自动化生成 PDF 表格
后端·c#
星释4 小时前
Rust 练习册 44:Trait 中的同名函数调用
开发语言·后端·rust
京东零售技术4 小时前
并发丢数据深度剖析:JED的锁机制与事务实战踩坑及解决方案
后端