Skip to content

会话技术


基本介绍

在 web 开发当中,会话指的就是浏览器与服务器之间的一次连接,我们就称为一次会话

在用户打开浏览器第一次访问服务器的时候,这个会话就建立了,直到有任何一方断开连接,此时会话就结束了。在一次会话当中,是可以包含多次请求和响应的

比如:打开了浏览器来访问 web 服务器上的资源(浏览器不能关闭、服务器不能断开)

第 1 次:访问的是登录的接口,完成登录操作

第 2 次:访问的是部门管理接口,查询所有部门数据

第 3 次:访问的是员工管理接口,查询员工数据

只要浏览器和服务器都没有关闭,以上 3 次请求都属于一次会话当中完成的

需要注意的是:会话是和浏览器关联的,当有三个浏览器客户端和服务器建立了连接时,就会有三个会话。同一个浏览器在未关闭之前请求了多次服务器,这多次请求是属于同一个会话。比如:1、2、3 这三个请求都是属于同一个会话。当我们关闭浏览器之后,这次会话就结束了。而如果我们是直接把 web 服务器关了,那么所有的会话就都结束了


会话跟踪技术

一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据

服务器会接收很多的请求,但是服务器是需要识别出这些请求是不是同一个浏览器发出来的。比如:1 和 2 这两个请求是不是同一个浏览器发出来的,3 和 5 这两个请求不是同一个浏览器发出来的。如果是同一个浏览器发出来的,就说明是同一个会话。如果是不同的浏览器发出来的,就说明是不同的会话。而识别多次请求是否来自于同一浏览器的过程,我们就称为会话跟踪

我们使用会话跟踪技术就是要完成在同一个会话中,多个请求之间进行共享数据

为什么要共享数据呢?

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

基本介绍


三个自动

为什么这一切都是自动化进行的?


测试代码

java
@Slf4j
@RestController
public class SessionController {

    //设置Cookie
    @GetMapping("/c1")
    public Result cookie1(HttpServletResponse response){
        response.addCookie(new Cookie("login_username","itheima")); //设置Cookie/响应Cookie
        return 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()); //输出name为login_username的cookie
            }
        }
        return Result.success();
    }
}

优缺点

优点

缺点

跨域介绍


现在的项目,大部分都是前后端分离的,前后端最终也会分开部署,前端部署在服务器 192.168.150.200 上,端口 80,后端部署在 192.168.150.100 上,端口 8080

我们打开浏览器直接访问前端工程,访问 url:http://192.168.150.200/login.html

然后在该页面发起请求到服务端,而服务端所在地址不再是 localhost,而是服务器的 IP 地址 192.168.150.100,假设访问接口地址为:http://192.168.150.100:8080/login

那此时就存在跨域操作了,因为我们是在 http://192.168.150.200/login.html 这个页面上访问了 http://192.168.150.100:8080/login 接口

区分跨域的维度(三个维度有任何一个维度不同,那就是跨域操作)

协议

IP / 协议

端口

跨域举例

http://192.168.150.200/login.html ----------> https://192.168.150.200/login [协议不同,跨域]

http://192.168.150.200/login.html ----------> http://192.168.150.100/login [IP 不同,跨域]

http://192.168.150.200/login.html ----------> http://192.168.150.200:8080/login [端口不同,跨域]

http://192.168.150.200/login.html ----------> http://192.168.150.200/login [不跨域]

Seesion

获取 Session


如果我们现在要基于 Session 来进行会话跟踪,浏览器在第一次请求服务器的时候,我们就可以直接在服务器当中来获取到会话对象 Session。如果是第一次请求 Session ,会话对象是不存在的,这个时候服务器会自动的创建一个会话对象 Session 。而每一个会话对象 Session ,它都有一个 ID(示意图中 Session 后面括号中的 1,就表示 ID),我们称之为 Session 的 ID


查找 Session


这样我们是不是就可以通过 Session 会话对象在同一次会话的多次请求之间来共享数据了?好,这就是基于 Session 进行会话跟踪的流程

测试代码

java
@Slf4j
@RestController
public class SessionController {

    @GetMapping("/s1")
    public Result session1(HttpSession session){
        log.info("HttpSession-s1: {}", session.hashCode());

        session.setAttribute("loginUser", "tom"); //往session中存储数据
        return Result.success();
    }

    @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);
    }
}

(3)通过控制台日志输出可以知道,两次请求,获取到的 Session 会话对象的 hashcode 是一样的,就说明是同一个会话对象。而且,第一次请求时,往 Session 会话对象中存储的值,第二次请求时,也获取到了。 那这样,我们就可以通过 Session 会话对象,在同一个会话的多次请求之间来进行数据共享了。

优缺点

优点

Session 是存储在服务端的,安全

缺点

服务器集群环境下无法直接使用 Session

⚠️ 注意点

Session 无法应用于集群

首先第一点,我们现在所开发的项目,一般都不会只部署在一台服务器上,因为一台服务器会存在一个很大的问题,就是单点故障。所谓单点故障,指的就是一旦这台服务器挂了,整个应用都没法访问了

所以在现在的企业项目开发当中,最终部署的时候都是以集群的形式来进行部署,也就是同一个项目它会部署多份。比如这个项目我们现在就部署了 3 份

而用户在访问的时候,到底访问这三台其中的哪一台?其实用户在访问的时候,他会访问一台前置的服务器,我们叫负载均衡服务器,我们在后面项目当中会详细讲解。目前大家先有一个印象负载均衡服务器,它的作用就是将前端发起的请求均匀的分发给后面的这三台服务器

此时假如我们通过 session 来进行会话跟踪,可能就会存在这样一个问题。用户打开浏览器要进行登录操作,此时会发起登录请求。登录请求到达负载均衡服务器,将这个请求转给了第一台 Tomcat 服务器

好。此时假如又执行了一次查询操作,要查询部门的数据。这次请求到达负载均衡服务器之后,负载均衡服务器将这次请求转给了第二台 Tomcat 服务器,此时他就要到第二台 Tomcat 服务器当中。根据 JSESSIONID 也就是对应的 session 的 ID 值,要找对应的 session 会话对象

我想请问在第二台服务器当中有没有这个 ID 的会话对象 Session, 是没有的。此时是不是就出现问题了?我同一个浏览器发起了 2 次请求,结果获取到的不是同一个会话对象,这就是 Session 这种会话跟踪方案它的缺点,在服务器集群环境下无法直接使用 Session

JWT 令牌技术

基本介绍

这里我们所提到的令牌,其实它就是一个用户身份的标识,看似很高大上,很神秘,其实本质就是一个字符串

如果通过令牌技术来跟踪会话,我们就可以在浏览器发起请求。在请求登录接口的时候,如果登录成功,我就可以生成一个令牌,令牌就是用户的合法身份凭证。接下来我在响应数据的时候,我就可以直接将令牌响应给前端

接下来,在后续的每一次请求当中,都需要将令牌携带到服务端。携带到服务端之后,接下来我们就需要来校验令牌的有效性。如果令牌是有效性的,就说明用户已经执行了登录操作,如果令牌是无效的,就说明用户之前并未执行登录操作

此时,如果是在同一次会话的多次请求之间,我们想共享数据,我们就可以将共享的数据存储在令牌当中就可以了

优缺点

优点

支持 PC 端、移动端

解决集群环境下的认证问题

减轻服务器的存储压力(无需在服务器端存储)

缺点

需要自己实现(包括令牌的生成、令牌的传递、令牌的校验)

应用场景


JWT 令牌最典型的应用场景就是登录认证

在浏览器发起请求来执行登录操作,此时会访问登录的接口,如果登录成功之后,我们需要生成一个 jwt 令牌,将生成的 jwt 令牌返回给前端

前端拿到 jwt 令牌之后,会将 jwt 令牌存储起来。在后续的每一次请求中都会将 jwt 令牌携带到服务端

服务端统一拦截请求之后,先来判断一下这次请求有没有把令牌带过来,如果没有带过来,直接拒绝访问,如果带过来了,还要校验一下令牌是否是有效。如果有效,就直接放行进行请求的处理

在 JWT 登录认证的场景中我们发现,整个流程当中涉及到两步操作

1. 在登录成功之后,要生成令牌

2. 每一次请求当中,要接收令牌并对令牌进行校验