剖析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;
    }

}
相关推荐
哎呦没10 分钟前
SpringBoot框架下的资产管理自动化
java·spring boot·后端
2401_8576009513 分钟前
SpringBoot框架的企业资产管理自动化
spring boot·后端·自动化
NiNg_1_2344 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
Chrikk6 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*6 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue6 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man6 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
customer087 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源