一个 bug 和一段废话

127 阅读5分钟

基础真的很重要

因为学习需要,最近在学习 Node 的相关知识,在看双越老师的课程,双越老师从原生讲到 Express 和 Koa,好巧不巧,在学习原生 Node.js 的时候我就卡住了(羞愧)。在学长的帮助下,花了好大功夫解决了一个错误,但是,在排查错误的过程中学到了一些东西,记录一下

1. 前置知识

1.1 cookie

大家伙都知道咱们的 HTTP 协议是无状态,也就是说每一次的 HTTP 请求都是独立自主的。这会导致什么问题呢?比如说用户在输入账号密码,浏览器发送 HTTP 请求登录后,因用户操作,需要再发送一次请求,获取用户数据记录。这个时候因为安全问题,必须得有账号密码咱才能给你记录啊,又因请求独立,咱也不知道用户密码账号?难不成让用户再输一次,万万不可,这个时候 cookie 就派上用场了。我相信不会有小伙伴说咱一次性把所有数据请求过来不就完了的玩笑的。

cookie 在浏览器和服务器之间的关系,大概就是下面这张图这么个情况

image-20210925213612874.png

在浏览器终端打印一下 ,就会发现,cookie 本质上就是一段字符串,它会保存在浏览器,我们可以通过 HTTP 的响应头中的 Set-Cookie 创建/修改它。HTTP在发送请求的时候也会带上它,我们可以在服务端可以通过 req.headers.cookie 获取。

image-20210925211641020.png

cookie 有一些值得注意的属性,请看下面这张截图

image-20210925213834288.png

NameValue 自然就不用说了,虽然 cookie 是字符串,但是我们写的时候是把他写成键值对的形式的。

domain 指定可以访问该 cookie 得域名,path 指定路径,一般是 “/”,还有一个就是 httpOnly 设置为 true 的话,JavaScript 脚本就不能读取 cookie 了。这能有效防止 xss攻击

cookie 虽好,设置了 httpOnly 之后也具有了一定安全性,但是仍然存在一些问题,比如

  • cookie 有大小限制,一般为 4kb
  • 我们不可能把用户密码啥的直接存在 cookie,存在风险

这个时候就到我们的 session 了

1.2 session

session 并不是一种技术,只是一种解决方案,当然有很多基于这种解决方案封装的工具。前面我们提到,在 cookie 里面不存重要信息,那我们就存一个 id ,服务端拿到 id 去 session 里面找对应的用户信息。其实 seesion 就是一个全局对象,可以保存很多用户的信息,用户请求时,就通过 cookie 拿 id 来取值。

2.惊现 bug

描述一下老师代码的思路,设置一个全局的空对象 SESSION_DATA 来保存众多用户的数据。,每一次请求,服务端都会从请求中获取解析 cookie 并将它放在请求体上边,然后拿着 cookie 里面的 idSESSION_DATA 里面去取值,并将他它赋值给请求体上的一个自定义属性,在代码里面是这样写的 req.session = SESSION_DATA[userId]

为了验证效果,设置了两种请求,登录请求:把用户名密码啥的赋值给 req.session ,验证登录请求:打印 req.session

测试开始,首先发送登录验证请求,打印 {},然后发送登录请求,这个时候 req.session 已经被赋值了用户名密码啥的,然后再发送登录验证请求,老师说这个时候应该打印出用户输入的账号密码。而我自己实验一直还是打印空对象。然后找 bug。在学长的帮助下发送是设置 cookie 的时候键名大小写问题。但是我的疑惑却还是更加大了。

前面说过,用户的信息都是保存在 SESSION_DATA 里面的,但是至始至终,我们都只从 SESSION_DATA 获取数据,就是这行代码 req.session = SESSION_DATA[userId] 。我们修改数据,也只是修改了 req.sessionreq.session 只是我们给请求体 req 上边加的一个自定义属性啊。每次请求都会有独立的请求体,那我们在上一次请求保存在 req.session 里面的数据,下一次请求怎么能获取呢!

然后就是找啊找啊找“答案”,发现,玄机还是在这行代码 req.session = SESSION_DATA[userId]。其实这是基础的 JS 知识(羞愧),就是引用类型的赋值。

3.引用类型

3.1 原始数据类型

  • undefined:typeof instance === "undefined"
  • Boolean:typeof instance === "boolean"
  • Number:typeof instance === "number"
  • String:typeof instance === "string
  • BigInt:typeof instance === "bigint"
  • Symbol :typeof instance === "symbol"
  • null:typeof instance === "object"。//这个是 object 哦

3.2 引用数据类型

就是 object 了,包括 Array,Function,date 等。而且学过任何一种语言的小伙伴应该知道,引用数据类型是在栈里面存一个地址,地址指向堆内存,我们的这行代码 req.session = SESSION_DATA[userId] 就是将两个数据栈中的地址变成一样的了,改变任何一个,另一个都会改变。至此 bug 解决,疑惑搞定,继续开搞。

其实感觉没看过双越老师那个课的小伙伴会感觉说天书一样,你又没代码, bb 个啥呢一个人。hhhh,其实感觉代码贴出来太多了,而且更加没啥看的,就当留给自己看了。