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 是以键值对的形式保存在浏览器。以=相关联,以;分隔。

1
2
3
4
// 查看cookie
document.cookie;
// 新增cookie
document.cookie = "name = Tom";

localStorage

同源问题:非同源的不能操作修改 localStorage。

1
2
3
4
5
6
7
8
9
10
// 设置localStorage
localStorage.setItem("name", "Tom");
// 查看localStorage
localStorage.getItem("name"); // Tom
// 设置相同键名,会覆盖之前的值
localStorage.setItem("name", "Jack"); // name被覆盖为Jack
// 删除指定localStorage
localStorage.removeItem("name");
// 删除所有localStorage
localStorage.clear();

强制缓存,协商缓存

HTTP 缓存都是从第二次请求开始的:
第一次请求资源时,服务器返回资源,并在 response header 中回传资源的缓存策略;
第二次请求时,浏览器判断这些请求参数,击中强缓存就直接 200,否则就把请求参数加到 request header 头中传给服务器,看是否击中协商缓存,击中则返回 304,否则服务器会返回新的资源。
image.png

强制缓存

在缓存未失效时,直接使用浏览器缓存数据,不再请求服务端.生效时,状态码是 200.
cache-control 优先级 > expires 优先级。

协商缓存

第一次请求服务器,或者cache-controlExpire过期,第二次请求就会与服务器协商,与服务端对比资源是否修改更新.没有修改,则返回 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
2
3
4
5
6
7
8
9
10
11
12
13
14
// 当前页面是test2.jsxx.com;需要请求test.jsxx.com/xx.php,存在跨域
// 利用test2.jsxx.com可以请求该资源,创建一个相同基础域名的iframe,
// 利用iframe去请求资源
document.domain = "jsxx.com";

var iframe = document.createElement("ifarme");
iframe.src = "http://test.jsxx.com/index.html"; // 这个域名的文件设置相同的基础域名
iframe.id = "myIframe";
iframe.style.display = "none";
iframe.onload = function () {
var contentWindow = document.getElementById("myIframe").contentWindow;
// 发起请求
};
document.body.appendChild(iframe);

window.name + iframe

同一个窗口不同的页面,共享同一个 window。
在当前窗口的当前页面,设置 window.name = ‘123’。跳转到另一个页面,这个页面也可以拿到 window.name=’123’。

同一个窗口下的 iframe 如果和父级主窗口不同源,是拿不到 window 的信息的。
解决方法:让该不同源的 iframe 跳转到一个和父级同源的 iframe。
注意:要在跳转之前,请求数据,使用 window.name 进行保存。跳转的同时,获取 iframe 保存的 name。(iframe 保存的是请求的跨域的那个 window 的 name)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var flag = false

var iframe = document.createElement('iframe');
var getDatas = function(){
if(flag){
// 跳转后的加载走这里,contentWindow存的是同一个name,可以获取了
var data = iframe.contentWindow.name;
console.log(JSON.parse(data));
}else{
// 第一次加载,走这里,跳转到同源地址
flag = true;
setTImeout(()=>{
ifrmae.contentWindow.location = 'index2.html'
},500)
}
}

// 跨域获取的资源链接
iframe.src = 'http://test.jsxx.com/index.html';
// 每次加载时,获取数据
iframe.onload = getDatas();

document.body.appendChild(iframe)

// index.html
// 获取的数据保存到window上
window.name = 'xxx'

postMessage+iframe

不经常用原因:

  1. 伪造数据端漏洞
  2. XSS 攻击
  3. 兼容性问题

JSONP

客户端期望:{"name": "Jack", "age": "18"}
JSONP 实际返回的:callback({"name": "Jack", "age": "18"})
利用 script 标签允许跨域,在当前页面定义回调函数。在新建的 script 标签上指定要跨域的 src,src 所对应的文件里执行 ajax 请求,成功的回调就是页面中定义的回调。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//* 原理

// 当前页面
<script type="text/javascript">
// 定义回调函数
function getData(data){
console.log(data)
}
</script>
// 新建一个用于跨域的标签,页面加载的时候会执行
<script src="http://test.jsxx.com/jsonp/jsonp.js"></script>

// 跨域标签所指向的文件 jsonp.js
$.ajax('xxx'){
success(data){
// 执行回调
getData(data)
}
}

实际操作是使用新建 script 的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//* 实际操作
<script type="text/javascript">
var oBtn = document.getElementById('btn');

oBtn.onClick = function(){
oScript = document.createElement("script")
oScript.src = "http://test.jsxx.com/jsonp/jsonp.php?cb=test"
document.body.appendChild(oScript)
document.body.removeChild(oScript)


// 使用jQuery封装的方法
$.ajax({
url: "http://test.jsxx.com/jsonp/jsonp.php",
type: "GET",
dataType: "JSONP",
jsonp: "cb",
success: function(data){
console.log(data)
}
})
}
</script>

cors

服务端设置Access-Control-Allow-Origin,可以设置哪些域名可以访问资源.

nginx 反向代理

搭建一个中转 nginx 服务器,用于转发请求.
理解: 设置一个中转用的代理服务器,所有接口都访问这个服务器,在这个服务器内设置对应的目标服务器.选择目标服务器获取数据,发送给客户端.
实现思路:通过 nginx 配置一个代理服务器(域名与 domain1 相同,端口不同)做跳板机,反向代理访问 domain2 接口,并且可以顺便修改 cookie 中 domain 信息,方便当前域 cookie 写入,实现跨域登录。

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80; # 监听的端口
server_name localhost; # 用户访问 localhost,反向代理到 http://webcanteen.com
location / {
proxy_pass http://webcanteen.com # 即需要代理访问的地址
}
# 也可以写其他的比如/api
location /api {
proxy_pass http://api.web.com # 这就可以将请求/api的接口代理到这个地址
}
}

保持前后端实时通信的方法

  1. websocket
  2. event-source
  3. ajax 轮询
  4. 永久帧

websocket

优点: 它是在单个 tcp 连接上进行的全双工通讯协议,客户端和服务器只进行一次握手就可以进行持续双向的数据传输.节约资源和带宽.
缺点: 1.兼容性.2.不支持断线重连,需要手写心跳连接的逻辑.3.通信机制复杂

event-source

优点:(1)只需一次请求,便可以 stream 的方式多次传送数据,节约资源和带宽 (2)相对 WebSocket 来说简单易用 (3)内置断线重连功能(retry)
缺点: (1)是单向的,只支持服务端->客户端的数据传送,客户端到服务端的通信仍然依靠 AJAX,没有”一家人整整齐齐“的感觉(2)兼容性令人担忧,IE 浏览器完全不支持

AJAX 轮询

优点:兼容性良好,对标低版本 IE
缺点:请求中有大半是无用的请求,浪费资源

持久登录

单点登录机制:不同源的站点,相同基础域名,一个登录,其他都登录。
image.png

后端加密的操作

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
2
3
cookie auth= ident_code:token
// 后端设置cookie
setCookie('auth',ident_code:token, 过期时间, '/'【有效地址】, '.baidu.com'【有效域】)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function get(key, cb) {
var cookieArray = document.cookie.split(";");

for (var i = 0; i < cookieArray.length; i++) {
var cookieItem = cookieArray[i];

var cookieItemArray = cookieItem.split("=");

if (cookieItemArray[0] == key) {
cb(cookieItemArray[1]);
return this;
}
}
cb(undefined);
s;
return this;
}

文件上传

同步上传
image.png

异步上传

image.png
image.png
image.png

HTTP 版本

HTTP/1.1

  • 默认持久连接(Connection:keep-alive)
  • 增加管道机制(支持多个请求同时发送)
  • 增加 PUT/PATCH/DELETE/OPTIONS 等请求方式
  • 增加 HOST 字段
  • 增加 100 状态码,支持只发送头信息
  • 增加身份认证机制
  • 支持传送内容的一部分和断点续传
  • 增加 24 个状态码

HTTP/2.0

  • 增加双工模式(同时发送和处理多个请求)
  • 服务器推送(服务端会把客户端需要的资源一起推送到客户端)
  • 头部信息压缩机制(每次请求把所有信息发给服务端【HTTP 协议不带状态】)
  • 二进制协议(头信息与数据体使用二进制进行传输)
  • 多工(先发送已处理好的部分,再响应其他请求,最后再处理没解决的地方)

HTTP1 和 HTTP2 的区别

  1. http1 是明文传送,http2 是二进制传送
  2. http2 支持多路复用
  3. http2 支持头部压缩
  4. http2 支持服务器推送

HTTP 和 HTTPS 的区别

  1. http 是超文本传输协议,信息是明文传输,https 是具有安全性的 TLS 加密传输协议
  2. https 需要 ca 申请证书
  3. http 端口使用 80,https 使用 443
  4. https 的传输内容都被 SSL/TLS 加密,且运行在 SSL/TLS 上,SSL/TLS 运行在 TCP 连接上。

加密方式

受 TLS 协议保护的通信过程:先对传输的数据进行了加密(使用对称加密算法)。并且对称加密的密钥是为每一个连接唯一生成的(基于 TLS 握手阶段协商的加密算法和共享密钥),然后发送的每条消息都会通过消息验证码(Message authentication code, MAC),来进行消息完整性检查,最后还可以使用公钥对通信双方进行身份验证

非对称加密

简单理解就是

  1. 服务端发证书给客户端
  2. 客户端获取 CA 证书中的公钥.
  3. 客户端生成对称密钥, 用公钥加密私钥 =>发给服务端.
  4. 服务端用私钥解密
  5. 双方使用这个对称秘钥通信,这时候加密方式就是对称加密

缺点

证书可能是伪造的,可能被篡改

解决方法

数字签名和摘要.
对传输内容通过 hash 算法计算出一段固定的串.用私钥加密,就是数字签名.
这个只能用 CA 的公钥解密.防止 CA 被篡改或者伪造.

请求网页的过程

xxxxxxxx

URI

URI:统一资源标识符,用来标识唯一的一个资源。
URL:统一资源定位符。URL 可以标识一个资源,并且可以用地址定位资源。
URN:统一资源命名。通过名字来表示资源。用名称定位资源。
URL 肯定是 URI,URI 不一定是 URL,也可能是 URN。
URI 是比较宽范的,包含了 URL。

URL

  1. 资源标识
  2. 具有资源定位功能
  3. 指明了获取资源的协议

URL 组成

  1. 协议名称(scheme):http,https,ws
  2. 主机名(host):域名或 IP
  3. 端口号(port):80(http 默认),443(https 默认)
  4. 路径(path):index/index.html
  5. 查询字符串(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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let url = new URL("https://google.com/search");

url.searchParams.set("q", "test me!"); // 添加带有一个空格和一个 ! 的参数

alert(url); // https://google.com/search?q=test+me%21

url.searchParams.set("tbs", "qdr:y"); // 添加带有一个冒号 : 的参数

// 参数会被自动编码
alert(url); // https://google.com/search?q=test+me%21&tbs=qdr%3Ay

// 遍历搜索参数(被解码)
for (let [name, value] of url.searchParams) {
alert(`${name}=${value}`); // q=test me!,然后是 tbs=qdr:y
}

TCP

传输控制协议
特点:面向连接(收发数据前,必须建立可靠的连接)
建立连接基础:三次握手
应用场景:数据必须准确无误的收发
HTTP 请求、FTP 文件传输、邮件收发
优点:稳定、重传机制、拥塞机制、断开连接
缺点:速度慢、效率低、占用资源、容易被攻击

UDP

类似于喇叭广播
特点:面向无连接(不可靠协议,无状态传输机制)
无连接信息发送机制
应用场景:无需确保通信质量且要求速度快,无需确保信息完整
消息收发,语音通话,直播
优点:安全,快速,漏洞少
缺点:不可靠,不稳定,易丢包
总结:只要目的源地址,端口号,地址,端口号 确定,就可以直接发送信息,但不保证一定能收到完整的信息。

三次握手

iShot2023-05-21 22.07.22.png
iShot2023-05-21 22.06.57.png

四次挥手

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 异同

  1. post 更安全:不会作为 url 一部分,不会被缓存,不会保存在浏览器记录中
  2. post 发送的数据量更大:GET 有长度限制
  3. post 能发送更多的数据类型:GET 只能发 ASCII 字符
  4. post 比 get 慢:POST 包含更多请求头,POST 请求先将请求头发给服务器,然后才发数据,GET 会缓存。
  5. 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

过期时间。

流程

  1. 发请求
  2. 有缓存,查看是否过期,不过期,直接读取缓存,返回 200
  3. 是否有 E-tag,Last-Modifed。有向服务端发送。E-tag(If-None-Match)优先级高,先验证它,一致后再验证 Last-Modifed(If-Modifed-Since).
  4. 资源未修改,返回 304,从缓存获取资源。失效,返回 200,重新返回资源。

history

histroy 栈
存储历史记录。
length 属性:存了多少条。

replaceState

用于替换 history 栈中的条目。有三个参数。

  1. 对象
  2. 网页名
  3. 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if (window.Worker) {
// 实例化
var worker = new Worker("worker.js");
// 信息
var message = { addThis: { num: 1, num2: 1 } };
// 传递信息
myWorker.postMessage(message);
// 接收信息
myWorker.onmessage = function (e) {
console.log(e);
};
}

// wokker.js
this.onmessage = function (e) {
if (e.data.addThs) {
var message = {
result: e.data.addThis.num1 + e.data.addThis.num2,
};
this.postMessage(message);
}
};