本文共 3173 字,大约阅读时间需要 10 分钟。
首先,Session 是什么?
简单地说,Session(会话) 是服务器用于辨识用户的一个手段
为 什 么 要 使 用 S e s s i o n \color{blue}{为什么要使用Session} 为什么要使用Session
HTTP请求是stateless的
也就是说谁都可以向服务器发送HTTP请求
接触过爬虫的朋友应该都能理解
像python中的requests.get(url)就相当于发送了一个HTTP请求
但是,这样单纯的交互只是一次性的
也就是,你发送一次HTTP请求,服务器回应你一次
你再次向同一个服务器发送HTTP请求时,服务器并不知道你是之前交流过的用户
这并不是我们想要的
因为一般情况下,我们与服务器之间的交互肯定不止一次
我从宏观角度举一个例子:
我登录上了的淘宝网
然后找到了想要的商品,正要购买时,网站提示我又要登录
??我不是已经登陆了吗??
嗯 就是这个意思
S e s s i o n 是 怎 么 使 用 的 \color{red}{Session是怎么使用的} Session是怎么使用的
那么,服务器怎么知道我就是之前和他有交互的用户呢?
可以通过Session(会话)
当我们第一次向服务器发送HTTP请求的时候,服务器会返回一个response给我们
在这个response中,会有一个session的ID,用于标识
(当然,不是所有服务器都会做这个操作,我这只讲有的情况)
这个其实我们可以直观看到
在Response Headers中你可以看见:
Connection: keep-aliveContent-Length: 142Content-Type: text/html;charset=UTF-8Date: Fri, 16 Oct 2020 13:24:08 GMTKeep-Alive: timeout=20Set-Cookie: JSESSIONID=E0DA20B59AF009AC7827C184946633F6; Path=/EE_learn; HttpOnly
Set-Cookie: JSESSIONID=E0DA20B59AF009AC7827C184946633F6; Path=/EE_learn; HttpOnly
没错,这个是Session (会话)的ID是服务器生成并且设置在Cookie中的
并且这个Set-Cookie操作只会在Session(会话)开始之时进行
你再次刷新页面查看的时候,在Response Headers中是无法再看到的
这时,你就得到Request Headers 中的Cookies 中去找了
Cookie: JSESSIONID=E0DA20B59AF009AC7827C184946633F6
那么,只要你每个HTTP请求都带上这个ID(这个工作浏览器会帮我们完成)
服务器就可以知道你是老用户了
不过,现在大部分的网站都不会只是通过Session的ID来辨识用户(不然,这个网站三下两下得被爬虫搞崩)
还会在Cookies中添加类似SessionID的别的一些凭证(很多爬虫就是卡在了这一关上)
对了,不要被我上面举的例子误导,SessionID并一定是 JSESSIONID
JSESSIONID 只是Tomcat服务器上Servelet 生成的SessionID
其他服务器各有不同
S e s s i o n 的 生 命 周 期 \color{red}{Session的生命周期} Session的生命周期
虽然我上面一直都在讨论SessionID,但实际上Session是一个对象
它有自己的生命周期
当第一次请求时,服务器会生成
那么什么时候结束呢?
有三个方法
1.timeout
服务器在设置SessionID的时候还设置了一个timeout
时间一过,Session便会被销毁
不同服务器timeout规则可能不一样
对于Tomcat 是可以在web.xml中配置这个timeout的
30
单位是分钟
2.客户端销毁Session
我们退出浏览器就可以销毁当前的Session(会话)了
注意是退出浏览器,而不是单单关闭页面
(复制好链接,退出浏览器,打开浏览器,粘贴链接,此时相当于第一次请求,会生成新的Session)
对了,有一些网站,你尽管退出了浏览器,但重新进入网站,还是处于登录状态,多半是因为Cookies
关闭浏览器会销毁的是当前Session,而Cookies是浏览器可以保存在本地的
3.服务器端销毁Session
调用Session对象的invalidate()方法即可销毁当前Session
补充:
其实,这个SessionID不一定是储存在Cookies里
还可以在URL的参数中
在response对象中便有一个encodeURL()方法用于生成SessionID(也是在第一次请求的情况下)
在doGet()方法中可以这么调用encodeURL()
response.encodeURL(HttpUtils.getRequestURL(request).toString())
不 建 议 使 用 S e s s i o n 的 A t r r i b u t e \color{red}{不建议使用Session的Atrribute} 不建议使用Session的Atrribute
确实,Session提供了setAttribute()和getAttribute() 方法
但是,却不建议使用
包括context对象中的Atrribute 一样不建议使用
因 为 C o n t e x t 和 S e s s i o n 都 不 是 线 程 安 全 的 \color{blue}{因为Context和Session都不是线程安全的} 因为Context和Session都不是线程安全的
理解这个之前,还得先了解一下Servlet的机制
每个Servlet只有一个实例,当同时有多个请求时,Tomcat容器会为每个请求分配一个线程
(Tomcat容器是有一个线程池用来处理请求的)
然后,用户与服务器之间的交互其实是多线程并发的
那么对于Session这样的共享变量来说
则需要考虑考虑同步的问题
而Tomcat 本身对于Session并不会上锁
如果我们忽略了这一点的话,就有可能会出现错误
真要用Session的Attribute也行,得自己上锁就是了
(我有实际测试的例子,但是。。一下关联到几个文件,说明起来有点麻烦,有需要再留言吧)
那。。。Attribute还有什么用?
r e q u e s t 里 的 A t t r i b u t e 是 线 程 安 全 的 \color{blue}{request里的Attribute是线程安全的} request里的Attribute是线程安全的
容器会为每个请求创建两个对象:HttpServletResponse and HttpServletRequest
也就是我们常见的request和response
当同时有多个请求的时候,容器会各自为其分配线程
同时,还会为每个线程创建各自的HttpServletResponse和HttpServletRequest的实例
即request是每个线程里的局部变量(其实是个对象啦,只是侧重点在局部)
和session这个共享变量是有区别的
所以,使用request里的Attribute时,就不用担心线程安全问题了
我们可以放心地用request在多个servlet之间传递
各个编程语言有各个编程语言自己的风格
转载地址:http://kliyb.baihongyu.com/