摘要:前言最近看了一些同学的面经,发现无论什么技术岗位,还是会问到和的区别。所有学技术的同学都知道和函数怎么用,知道和的区别就是是储存在服务端的,是存储在浏览器的。的诞生是为了能让无状态的报文带上一些特殊的数据,让服务端能够辨识请求的身份。
1 前言
最近看了一些同学的面经,发现无论什么技术岗位,还是会问到 Session 和 Cookie 的区别。
所有学技术的同学都知道 Session 和 Cookie 函数怎么用,知道 Session 和 Cookie 的区别就是 Session 是储存在服务端的,Cookie 是存储在浏览器的。
但是实际上是什么东西,一些刚学习技术的同学估计还是模糊,我刚学 PHP 的时候,这种感觉特别明显。PHP 中 Session 和 Cookie 的操作只要操作 $_COOKIE 和 $_SESSION 数组就可以了,而且操作方式和功能一模一样,搞得我一脸懵逼。
最后,还是自己实现了一个 Session 操作类才恍然大悟,实质上就是两个不同的存储对象嘛。
2 CookieCookie 的诞生是为了能让无状态的 HTTP 报文带上一些特殊的数据,让服务端能够辨识请求的身份。
对于 Cookie 的概念就不多说了,Cookie 说简单点就是浏览器上的一个 key-value 存储对象,通过开发者工具直接看到 Cookie 的内容(F12)
写入数据方式
Cookie 写入数据的方式是通过 HTTP 返回报文 Header 部分 Set-Cookie 字段来设置,一个带有写 Cookie 指令的的 HTTP 返回报文如下
HTTP/1.1 200 OK Set-Cookie: SESSIONID=e13179a6-2378-11e9-ac30-fa163eeeaea1; Path=/ Transfer-Encoding: chunked Date: Tue, 29 Jan 2019 07:12:09 GMT Server: localhost
上述报文 Set-Cookie 指示浏览器设置 key 为 SESSIONID,value 为 e13179a6-2378-11e9-ac30-fa163eeeaea1 的 Cookie
获取数据方式
浏览器在发送请求的时候会检查当前域已经设置的 Cookie,在 HTTP 请求报文 Header 部分的 Cookie 字段里面带上 Cookie 的信息。下面捉取了一段 HTTP 报文
GET http://10.0.1.24:23333/ HTTP/1.1 Host: 10.0.1.24:23333 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Cookie: SESSIONID=e13179a6-2378-11e9-ac30-fa163eeeaea1
从最后的 Cookie 字段看到,浏览器请求时带上了 key 为 SESSIONID,value 为 e13179a6-2378-11e9-ac30-fa163eeeaea1 的数据,后端直接解析 HTTP 报文就能获取 Cookie 的内容。
3 SessionSession 在代码里面的语意是记录客户端状态的一个存储对象,是同一个客户端请求共享的数组。这个存储对象可以是文件、缓存系统、数据库。
现在假设要使用 redis 来实现 Session 功能,那么就要求浏览器每次请求都要带一个相同的字符串作为身份信息,对应 redis 的 key,redis value 则为 Session 数组序列化的内容。
那么如何让浏览器每次请求都带一个身份信息呢,这就是 Session 和 Cookie 的关系,通过 Cookie 传递这个身份信息。流程如下
客户端请求
服务端检查 Header,发现没有 Cookie,于是生成一个 UUID
服务端处理数据,把部分数据(登录信息)存储到 redis 里面,UUID 为 key,用户 id 为 value
返回报文中,增加 Set-Cookie 字段,内容带上 UUID
浏览器收到报文,把 UUID 写进浏览器存储里面
浏览器再次请求,带上当前的域的 Cookie,就是这个 UUID
服务端通过 Cookie 字段获取到该 UUID,去 redis 里面获取用户的信息
...
4 手动实现 Session既然知道了 Session 的原理,我们手动实现一个 Session 操作类,采用文件保存的方式。http 框架采用 web.py,安装方式如下
pip install web.pySession类
我们要实现的这个类就叫 Session
class Session: def __init__(self): self.session_id = None # session 数组 self._items = dict() self._load()
我们所有 session 文件放在 sessions 目录下,文件名为对应的 session id,内容为 Session 数组序列化的字符串。在初始化对象的时候通过获取名为 SESSIONID 的 Cookie,如果没有就生成一个新的。
def _load(self): SESSIONID = web.cookies().get("SESSIONID", None) if not SESSIONID: SESSIONID = uuid.uuid() self.session_id = SESSIONID self._loadFromDisk()
获取到 SESSIONID 后,检查 sessions 目录下有没有对应的文件,如果有就读取并反序列化
def _loadFromDisk(self): """ 从文件加载 SESSION """ file = "./sessions/%s" % self.session_id if os.path.exists(file): f = open(file, "rb") self._items = pickle.load(f) f.close()
获取 Session 部分完成了,接下来就是保存 Session,我们要把 SESSIONID 写进 Cookie 里面,这样才能在下次请求时获取到对应的 SESSIONID
def _setSessionCookie(self): """ Session id 写入 cookie """ web.setcookie("SESSIONID", self.session_id)
最后把 Session 的内容保存到文件里面
def _saveToDisk(self): """ 保存 SESSION 到文件 """ f = open("./sessions/%s" % self.session_id, "wb") pickle.dump(self._items, f) f.close()功能测试
我们新建一个文件,叫 server.py,写入测试代码
#!/usr/bin/env python # -*- coding: utf-8 -*- # 测试 session import web import time from session import Session urls = ( "/", "index" ) app = web.application(urls, globals()) class index: def GET(self): session = Session() if "login_time" not in session: session["login_time"] = int(time.time()) return "login time: %s" % session["login_time"] if __name__ == "__main__": app.run()
在同一目录新建 session.py 文件,写入 Session 类代码
#!/usr/bin/env python # -*- coding: utf-8 -*- # Session import os import web import uuid try: import cPickle as pickle except ImportError: import pickle class Session: __instance = None def __new__(cls): """ 单例模式 """ if cls.__instance is None: cls.__instance = object.__new__(cls) return cls.__instance else: return cls.__instance def __init__(self): self.session_id = None # session 数组 self._items = dict() self._load() def __contains__(self, key): return key in self._items def __getitem__(self, key): return self._items.get(key, None) def __setitem__(self, key, value): self._items[key] = value return True def __delitem__(self, key): if key in self._items: del self._items[key] return True def __del__(self): """ 析构函数,结束请求时执行 """ self._setSessionCookie() self._saveToDisk() def _load(self): SESSIONID = web.cookies().get("SESSIONID", None) if not SESSIONID or SESSIONID is None: SESSIONID = uuid.uuid() self.session_id = SESSIONID self._loadFromDisk() def _loadFromDisk(self): """ 从文件加载 SESSION """ file = "./sessions/%s" % self.session_id if os.path.exists(file): f = open(file, "rb") self._items = pickle.load(f) f.close() def _setSessionCookie(self): """ Session id 写入 cookie """ web.setcookie("SESSIONID", self.session_id) def _saveToDisk(self): """ 保存 SESSION 到文件 """ f = open("./sessions/%s" % self.session_id, "wb") pickle.dump(self._items, f) f.close()
再在同一目录新建 sessions 目录,存放我们的 Session 文件
mkdir sessions
启动服务
[service@chengqm mysession]$ python server.py 23333 http://0.0.0.0:23333/
浏览器发起请求
查看 Cookie
查看 Session 文件内容
[service@chengqm mysession]$ cat sessions/e13179a6-2378-11e9-ac30-fa163eeeaea1 (dp1 S"login_time" p2 I1548749002 s.
可以多次刷新和更换浏览器测试,测试结果是符合我们对 Session 的预期,简陋版 Session 类功能就算实现了。
5 如果禁止 Cookie 是否可以获取 Session这是一道面试题,当年竟然能用这个问题问倒过一些朋友,还是有些意思的
从前面可以知道,SESSIONID 是通过 Cookie 来传递,如果 Cookie 禁止了,还能获取 SESSIONID 吗? 答案是可以的
既然 Cookie 禁止了,那么我们就可以用参数的方法传递 SESSIONID,后端返回的时候,增加一个返回参数,叫 SESSIONID,然后前端存储到 localstorage 里面
前端请求的时候,去 localstorage 获取SESSIONID,在请求参数里面增加这一个参数
后端 Session 处理,先尝试从 Cookie 中获取 SESSIONID,如果获取不到,再尝试从请求参数中获取 SESSIONID
这样,就算禁止 Cookie 也是能获取 Session 的。
6 总结最后,我们得出 Session 和 Cookie 区别和联系
区别
Cookie 是浏览器端的存储对象,有容量限制,通过 HTTP 报文与后端交互
Session 是服务端的存储对象,实现的方式可以有文件系统、缓存系统、数据库
联系
Session 和 Cookie 都是为了实现 HTTP 请求带上客户端状态的方法
Session 大多数情况下都是依赖 Cookie 来传递 Session Id
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/43107.html
摘要:前言最近看了一些同学的面经,发现无论什么技术岗位,还是会问到和的区别,而搜索出来的答案并不能让我们装得一手好逼,那就让我们从报文的角度来撸一波,从而搞明白他们的区别。所以,和分开发送是部分浏览器或框架的请求方法,不属于必然行为。 1 前言 最近看了一些同学的面经,发现无论什么技术岗位,还是会问到 get 和 post 的区别,而搜索出来的答案并不能让我们装得一手好逼,那就让我们从 HT...
摘要:联调测试,无需依赖他人。针对以上问题,有两种解决方法,一个是自己搭建私有服务,另一个是用云服务的镜像管理平台如阿里云的容器镜像服务。利用,先对阿里云的服务进行登录。推送后,就能在阿里云的仓库上看到这个镜像。 Docker简述 Docker是一种OS虚拟化技术,是一个开源的应用容器引擎。它可以让开发者将应用打包到一个可移植的容器中,并且该容器可以运行在几乎所有linux系统中(Windo...
摘要:示例使用作为深度,展开任意深度的嵌套数组会移除数组中的空项首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。注意对象数组不能使用方法来检测。也就是返回值返回一个新的对象,该对象包含数组中每个索引的键值对。 showImg(https://segmentfault.com/img/remote/1460000019303737?w=651&h=289); 前言 写久了业务代码的我...
摘要:年求职面经及总结我的求职之路差不多走到尽头了感觉真是精疲力尽了把这大半年的经历和面试总结写下来希望能给和我一样在求职路上煎熬的人一点帮助先说背景微电子科学与工程专业学过两门和相关的课程语言和单片机这个专业的唯一好处就是大部分人并不知道这个专 18年求职面经及总结 我的求职之路差不多走到尽头了,感觉真是精疲力尽了.把这大半年的经历和面试总结写下来,希望能给和我一样在求职路上煎熬的人一点帮...
摘要:年求职面经及总结我的求职之路差不多走到尽头了感觉真是精疲力尽了把这大半年的经历和面试总结写下来希望能给和我一样在求职路上煎熬的人一点帮助先说背景微电子科学与工程专业学过两门和相关的课程语言和单片机这个专业的唯一好处就是大部分人并不知道这个专 18年求职面经及总结 我的求职之路差不多走到尽头了,感觉真是精疲力尽了.把这大半年的经历和面试总结写下来,希望能给和我一样在求职路上煎熬的人一点帮...
阅读 1152·2021-11-24 10:43
阅读 3107·2021-11-22 09:34
阅读 3551·2021-10-08 10:04
阅读 3934·2021-09-23 11:58
阅读 3118·2019-08-30 15:44
阅读 485·2019-08-30 13:01
阅读 1155·2019-08-28 18:07
阅读 1450·2019-08-26 13:42