HTTP补档
cookie,session,token
服务器验证是前提。
cookie 保存在客户端,服务器不加密不保存;大小 4K,一般保存 sessionId。有 expire 和 max-age。
session 保存在服务端,服务器要加密并保存;
token 保存在客户端,服务器要加密不保存。明文传递给客户端,但是是加密的。
localStorage,sessionStorage,cookie
localStorage:整个浏览器有效,大小 10M.永久有效。
sessionStorage:只在当前 tab 页有效,相邻 tab 不同共享。大小 5M.tab 关闭即失效。
cookie:整个浏览器有效,大小 4K.根据设置的过期时间。可以保存在浏览器和服务端。
cookie
cookie 是以键值对的形式保存在浏览器。以=
相关联,以;
分隔。
1 | // 查看cookie |
localStorage
同源问题:非同源的不能操作修改 localStorage。
1 | // 设置localStorage |
强制缓存,协商缓存
HTTP 缓存都是从第二次请求开始的:
第一次请求资源时,服务器返回资源,并在 response header 中回传资源的缓存策略;
第二次请求时,浏览器判断这些请求参数,击中强缓存就直接 200,否则就把请求参数加到 request header 头中传给服务器,看是否击中协商缓存,击中则返回 304,否则服务器会返回新的资源。
强制缓存
在缓存未失效时,直接使用浏览器缓存数据,不再请求服务端.生效时,状态码是 200.
cache-control 优先级 > expires 优先级。
协商缓存
第一次请求服务器,或者cache-control
和Expire
过期,第二次请求就会与服务器协商,与服务端对比资源是否修改更新.没有修改,则返回 304 状态码,继续使用缓存数据.如果更新,则返回 200 状态码,并将缓存信息一并返回.
跨域
原因: 浏览器的同源策略(为了安全),即同端口,域名,协议.
跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
同源策略
限制的内容有:
- cookie, localstorage 等存储内容
- DOM 节点
- ajax 请求后浏览器返回的结果
允许的内容:
<img src="xxx">
<link href="xxx">
<script src="xxx">
解决方法
解决方案有 jsonp、cors、postMessage、websocket、Node 中间件代理(两次跨域)、nginx 反向代理、window.name + iframe、location.hash + iframe、document.domain + iframe
日常工作中,用得比较多的跨域方案是 cors 和 nginx 反向代理
iframe
通过 contentWindow 获取内部的 iframe 的 window。非同源的,父级无法获取子级的内容,跨域。
父级访问子级:iframe.contentWindow.xxx
子级访问父级:window.parent.xxx
访问祖先顶级:window.top.xxx
跨域解决
服务器中转请求
跨域是浏览器的限制,对服务器没有限制。
浏览器向同源的服务器发请求,该服务器向不同源的服务器发请求,返回请求内容,再返给浏览器。
相同基础域名+iframe
设置document.domain = 'xxx'
1 | // 当前页面是test2.jsxx.com;需要请求test.jsxx.com/xx.php,存在跨域 |
window.name + iframe
同一个窗口不同的页面,共享同一个 window。
在当前窗口的当前页面,设置 window.name = ‘123’。跳转到另一个页面,这个页面也可以拿到 window.name=’123’。
同一个窗口下的 iframe 如果和父级主窗口不同源,是拿不到 window 的信息的。
解决方法:让该不同源的 iframe 跳转到一个和父级同源的 iframe。
注意:要在跳转之前,请求数据,使用 window.name 进行保存。跳转的同时,获取 iframe 保存的 name。(iframe 保存的是请求的跨域的那个 window 的 name)。
1 | var flag = false; |
postMessage+iframe
不经常用原因:
- 伪造数据端漏洞
- XSS 攻击
- 兼容性问题
JSONP
客户端期望:{"name": "Jack", "age": "18"}
JSONP 实际返回的:callback({"name": "Jack", "age": "18"})
利用 script 标签允许跨域,在当前页面定义回调函数。在新建的 script 标签上指定要跨域的 src,src 所对应的文件里执行 ajax 请求,成功的回调就是页面中定义的回调。
1 | //* 原理 |
实际操作是使用新建 script 的方式。
1 | //* 实际操作 |
cors
服务端设置Access-Control-Allow-Origin
,可以设置哪些域名可以访问资源.
nginx 反向代理
搭建一个中转 nginx 服务器,用于转发请求.
理解: 设置一个中转用的代理服务器,所有接口都访问这个服务器,在这个服务器内设置对应的目标服务器.选择目标服务器获取数据,发送给客户端.
实现思路:通过 nginx 配置一个代理服务器(域名与 domain1 相同,端口不同)做跳板机,反向代理访问 domain2 接口,并且可以顺便修改 cookie 中 domain 信息,方便当前域 cookie 写入,实现跨域登录。
1 | server { |
保持前后端实时通信的方法
- websocket
- event-source
- ajax 轮询
- 永久帧
websocket
优点: 它是在单个 tcp 连接上进行的全双工通讯协议,客户端和服务器只进行一次握手就可以进行持续双向的数据传输.节约资源和带宽.
缺点: 1.兼容性.2.不支持断线重连,需要手写心跳连接的逻辑.3.通信机制复杂
event-source
优点:(1)只需一次请求,便可以 stream 的方式多次传送数据,节约资源和带宽 (2)相对 WebSocket 来说简单易用 (3)内置断线重连功能(retry)
缺点: (1)是单向的,只支持服务端->客户端的数据传送,客户端到服务端的通信仍然依靠 AJAX,没有”一家人整整齐齐“的感觉(2)兼容性令人担忧,IE 浏览器完全不支持
AJAX 轮询
优点:兼容性良好,对标低版本 IE
缺点:请求中有大半是无用的请求,浪费资源
持久登录
单点登录机制:不同源的站点,相同基础域名,一个登录,其他都登录。
后端加密的操作
username:md5(md5(username) + salt) => ident_code(身份识别码)
password:md5(md5(password) + salt)
salt:自定义的字符串
token:身份令牌,32/16 位随机字符串。每次登录就重新生成。
auth:ident_code:token
timeout:cookie 的过期时间
PC:30 天(如果不点连续登录就是 1 天) App:1 年
1 | cookie auth= ident_code:token |
前端获取 cookie
1 | function get(key, cb) { |
文件上传
同步上传
异步上传
HTTP 版本
HTTP/1.1
- 默认持久连接(Connection:keep-alive)
- 增加管道机制(支持多个请求同时发送)
- 增加 PUT/PATCH/DELETE/OPTIONS 等请求方式
- 增加 HOST 字段
- 增加 100 状态码,支持只发送头信息
- 增加身份认证机制
- 支持传送内容的一部分和断点续传
- 增加 24 个状态码
HTTP/2.0
- 增加双工模式(同时发送和处理多个请求)
- 服务器推送(服务端会把客户端需要的资源一起推送到客户端)
- 头部信息压缩机制(每次请求把所有信息发给服务端【HTTP 协议不带状态】)
- 二进制协议(头信息与数据体使用二进制进行传输)
- 多工(先发送已处理好的部分,再响应其他请求,最后再处理没解决的地方)
HTTP1 和 HTTP2 的区别
- http1 是明文传送,http2 是二进制传送
- http2 支持多路复用
- http2 支持头部压缩
- http2 支持服务器推送
HTTP 和 HTTPS 的区别
- http 是超文本传输协议,信息是明文传输,https 是具有安全性的 TLS 加密传输协议
- https 需要 ca 申请证书
- http 端口使用 80,https 使用 443
- https 的传输内容都被 SSL/TLS 加密,且运行在 SSL/TLS 上,SSL/TLS 运行在 TCP 连接上。
加密方式
受 TLS 协议保护的通信过程:先对传输的数据进行了加密(使用对称加密算法)。并且对称加密的密钥是为每一个连接唯一生成的(基于 TLS 握手阶段协商的加密算法和共享密钥),然后发送的每条消息都会通过消息验证码(Message authentication code, MAC),来进行消息完整性检查,最后还可以使用公钥对通信双方进行身份验证
非对称加密
简单理解就是
- 服务端发证书给客户端
- 客户端获取 CA 证书中的公钥.
- 客户端生成对称密钥, 用公钥加密私钥 =>发给服务端.
- 服务端用私钥解密
- 双方使用这个对称秘钥通信,这时候加密方式就是对称加密
缺点
证书可能是伪造的,可能被篡改
解决方法
数字签名和摘要.
对传输内容通过 hash 算法计算出一段固定的串.用私钥加密,就是数字签名.
这个只能用 CA 的公钥解密.防止 CA 被篡改或者伪造.
请求网页的过程
xxxxxxxx
URI
URI:统一资源标识符,用来标识唯一的一个资源。
URL:统一资源定位符。URL 可以标识一个资源,并且可以用地址定位资源。
URN:统一资源命名。通过名字来表示资源。用名称定位资源。
URL 肯定是 URI,URI 不一定是 URL,也可能是 URN。
URI 是比较宽范的,包含了 URL。
URL
- 资源标识
- 具有资源定位功能
- 指明了获取资源的协议
URL 组成
- 协议名称(scheme):http,https,ws
- 主机名(host):域名或 IP
- 端口号(port):80(http 默认),443(https 默认)
- 路径(path):index/index.html
- 查询字符串(query):
?a=1&b=2
URL 对象
创建 URL 对象,
1 | new URL("https://google.com/search?query=JavaScript"); |
searchParams 查询参数
查询参数是 URLSearchParams 类型.
有以下方法:
- append(name, value) —— 按照 name 添加参数,
- delete(name) —— 按照 name 移除参数,
- get(name) —— 按照 name 获取参数,
- getAll(name) —— 获取相同 name 的所有参数(这是可行的,例如 ?user=John&user=Pete),
- has(name) —— 按照 name 检查参数是否存在,
- set(name, value) —— set/replace 参数,
- sort() —— 按 name 对参数进行排序,很少使用,
……并且它是可迭代的,类似于 Map。
1 | let url = new URL("https://google.com/search"); |
TCP
传输控制协议
特点:面向连接(收发数据前,必须建立可靠的连接)
建立连接基础:三次握手
应用场景:数据必须准确无误的收发
HTTP 请求、FTP 文件传输、邮件收发
优点:稳定、重传机制、拥塞机制、断开连接
缺点:速度慢、效率低、占用资源、容易被攻击
UDP
类似于喇叭广播
特点:面向无连接(不可靠协议,无状态传输机制)
无连接信息发送机制
应用场景:无需确保通信质量且要求速度快,无需确保信息完整
消息收发,语音通话,直播
优点:安全,快速,漏洞少
缺点:不可靠,不稳定,易丢包
总结:只要目的源地址,端口号,地址,端口号 确定,就可以直接发送信息,但不保证一定能收到完整的信息。
三次握手
四次挥手
FIN-WAIT-1:等待远程 TCP 的中断请求,或先前的连接中断请求的确认
FIN-WAIT-2:从远程 TCP 等待连接中断请求
CLOSE-WAIT:等待从本地用户发送的连接中断请求
LAST-ACK:等待原来发向远程 TCP 的连接中断请求的确认
TIME-WAIT:等待足够的时间以确保远程 TCP 接收到连接中断请求的确认
CLOSED:没有任何连接状态
过程
第一次:客户端发送连接关闭报文 FN1,客户端进入终止等待(FIN-WAIT-1)
第二次:服务器收到连接关闭报文,发送确认报文 ACK1,服务器进入 CLOSE-WAIT 状态。
客户端收到服务端的确认请求,客户端进入终止等待 2(FIN-WAIT-2)状态,有可能有未发送完的信息
第三次:服务端确认数据发送完毕,向客户端发送连接关闭报文 FN1 ACK1,进入最后确认状态(LAST-ACK)
第四次:客户端收到服务端的连接关闭报文后,发出确认接收报文 ACK1,客户端进入时间等待状态(TIME-WAIT)
服务端接收到确认报文后,立即进入连接状态(CLOSED)。客户端在等待 2MSL 后关闭连接。
HTTP 报文
请求方式
PUT:上传资源。form 表单不支持,提交即存储的原则,需要配置服务器。
DELETE:删除资源。form 表单不支持,提交即存储的原则,需要配置服务器。
POST:修改资源。
GET:获取资源。
GET/POST 异同
- post 更安全:不会作为 url 一部分,不会被缓存,不会保存在浏览器记录中
- post 发送的数据量更大:GET 有长度限制
- post 能发送更多的数据类型:GET 只能发 ASCII 字符
- post 比 get 慢:POST 包含更多请求头,POST 请求先将请求头发给服务器,然后才发数据,GET 会缓存。
- post 的请求体是 formData,get 的请求体是 query sring。
状态码
1xx:信息,服务器收到请求。需要请求者继续执行操作。
2xx:成功,操作被成功接收并处理。
3xx:重定向,需要进一步的操作以完成请求。
4xx:客户端错误。请求包含语法错误或无法完成的请求。
5xx:服务端错误。服务器在处理请求的过程中发生错误。
304
缓存重定向。自上一次请求后,网页未被修改。
第一次发送请求,服务器会返回两个信息在响应头里。
E-Tag:唯一标识。
Last-Modifed:上次修改时间。
第二次发送请求,拿上这两个信息,重新命名,
If-Modify-Since:=> Last-Modifed 的信息。
If-None-Match: => E-Tag 的信息。
发给 服务端,服务端对比后,发现没有变化,就返回 304。浏览器从缓存中获取数据。
301 和 302
301:永久重定向,表示请求的资源分配了新的 url,以后应使用新 url。
302:临时重定向,请求的资源临时分配了新的 url(response 中 location 所指的地址),本次请求暂时使用新 url。服务器返回 302 时,也会返回 location,浏览器再次请求 location 中指定的地址,也就是浏览器请求了 2 次
401
为认证设计,返回 401 说明未通过认证。可能是认证出错或没有认证。
403
服务器拒绝请求。
这是授权相关,完成了认证,但是权限不够,不预授权。
404
未找到相应网页或资源。
Accept & Content-Type
q:相对品质因子,权重。从 0 到 1,表示优先级。默认 1。0 表示该类型不被浏览器接受。
Accept(请求头):type;q=value, type;q=value 分号是表示符号,逗号是分隔符号。
Content-Type(响应头):text/html;charset=UTF-8(Accept-Charset)
返回的资源类型与编码
Accept-Language:支持的语言。
Content-Language:返回的语言。
Accept-Encoding:接受的压缩格式。(gzip,defalte,br)
Content-Encoding:返回的压缩格式。(优化传输大小)
Connection:Keep-alive
长连接。
http1.1:默认开启。需要关闭:close。
Content-length
消息实体的传输长度。
referer
来源域名。告诉服务器是从哪个域名来的。
比如从百度搜 qq 官网,qq 官网请求头会显示 referer:www.baidu.com
场景:资源防盗链。拉取资源前判断 referer 是否是自己的域名。
浏览器缓存
pragma:no-cache(响应头) => 忽略缓存。每次请求都从走服务器。
cache-control
http1.0:cache-control 替代 pragma。向下兼容。
选项:
none-cache:忽略缓存(并非不要缓存)。
no-store:不要缓存。
max-age=316000:缓存的有效时长。从请求开始到过期的时间秒数。
public:响应可以被任何对象缓存。
private:响应只能被单个用户缓存,不能被共享。
Expires/max-age
过期时间。
流程
- 发请求
- 有缓存,查看是否过期,不过期,直接读取缓存,返回 200
- 是否有 E-tag,Last-Modifed。有向服务端发送。E-tag(If-None-Match)优先级高,先验证它,一致后再验证 Last-Modifed(If-Modifed-Since).
- 资源未修改,返回 304,从缓存获取资源。失效,返回 200,重新返回资源。
history
histroy 栈
存储历史记录。
length 属性:存了多少条。
replaceState
用于替换 history 栈中的条目。有三个参数。
- 对象
- 网页名
- url 地址
1 | history.replaceState("text name", null, "text.html"); |
pushState
和 replaceState 类似。也是三个参数。
区别:pushState 是添加,replaceState 是直接替换。
这两个方法修改 url 后,都不会引起渲染。
popState
在历史活动条目发送改变时触发。
在 pushState 和 replaceState 触发时不改变,手动点击后退时,触发。
1 | window.addEventListener("popState", () => {}); |
hashChange
当 hash 值改变,可以监听事件。、
web worker
开启一个新的线程。线程内只有 this,没有 window 和 document。
1 | if (window.Worker) { |