- 靓仔靓女们大家好,我们又见面了,公众号:java小杰要加油,这周来分享一篇关于
HTTP
协议相关的文章- 看完此文可以对
- HTTP报文格式、HTTP各种请求头,HTTP响应码、 cookie属性以及HTTPS为什么安全(涉及到三种加密方式) 有个清晰的认知
- 全文五千来字,强烈建议收藏,巩固基础
- 若文中涉及到的知识点有所偏差的话,还请大佬们指出,小杰感激不尽,冲冲冲!~
- 话不多说,直接开搞
HTTP简介
- 超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应
- 现在主要应用 http1.1 协议
- http是无状态协议,不会保存多次请求之间的关系,使用
cookie
做状态管理 - 持久连接节省通信量(HTTP1.1和部分HTTP1.0)
- 通过请求方法告知服务器意图,
get
,post
等
HTTP报文
- 用于HTTP协议交互的信息叫做HTTP报文
- 报文由报文首部和报文主体来组成,其中由空行分割
- 请求报文和响应报文的报文结构不一样,其中最大的区别就是在报文首部中,各有各的特定的首部
- 报文首部:服务器或者客户端需要处理的请求或者响应的内容及其属性
- 报文主体:被发送的数据
HTTP请求报文结构
- 由客户端发送的报文叫做请求报文
- 请求行:包含用于请求的方法,请求URI和HTTP版本
- 请求首部字段:请求报文里特有的字段(后文会提到)
- 通用首部字段:请求报文和响应报文都会用到的首部
- 实体首部字段:针对请求报文的实体部分使用的首部
- 其他:可能包含HTTP的RFC里未定义的首部(如Cookie等)
HTTP响应报文结构
- 由服务端发送的报文叫做响应报文
- 状态行:包含表明响应结果的状态码,原因短语和HTTP版本
- 响应首部字段:响应报文里特有的字段(后文会提到)
- 通用首部字段:请求报文和响应报文都会用到的首部
- 实体首部字段:针对响应报文的实体部分使用的首部
- 其他:可能包含HTTP的RFC里未定义的首部(如Set-Cookie等)
注:若HTTP首部字段重复了的话,不同的浏览器处理机制不一样
- 有些浏览器会优先处理第一次出现的字段
- 有些浏览器会优先处理最后一次出现的字段
HTTP响应码
2xx 成功
2xx的响应结果就代表请求被正常处理了
- 200 OK:表示客户端发来的请求被服务器正常处理了
- 204 Not Content:请求被成功处理,但是返回的响应报文不包含实体的主体部分
- 206 Partial Content:客户端进行范围请求,而服务器重新执行了这部分的GET请求
3xx 重定向
3xx的响应结果就表明浏览器需要执行某些特殊的处理以正确处理请求
- 301 Moved Permanently:永久重定向。表示请求的资源已经被分配了新的URI,以后应该使用新的URI
- 302 Found:临时重定向。代表资源只是暂时的移动了以后还可能会移动为新的URI
- 303 See Other:由于请求对应的资源存在着另一个URI,应使用GET方法定向获取请求的资源
- 304 Not Modified:客户端发送附带条件(请求首部中if开头的属性中的一种)的请求的时候,服务端允许访问资源,但是那些请求并没有满足,直接返回304,即服务端资源未改变,可以直接使用客户端未过期的缓存,304返回时,不包含任何响应的主体部分(虽然被划分为3xx里,但是和重定向没有任何关系)
4xx 客户端错误
4xx的响应结果就表明客户端是发生错误的原因所在
- 400 Bad Requset:请求报文中存在语法错误,请修改请求内容后再发送请求
- 401 Unauthorized:客户端未认证授权
- 403 Forbidden:服务端禁止客户端访问此资源
- 404 Not Found:URL写错了,找不到此路径
5xx 服务器错误
5xx的响应结果就表明服务器本身发生错误
- 500 Internal Server Error:服务器内部故障,可能是bug导致的
- 503 Service Unavaliable:服务器暂时不可用(停机维护或者超负载),如果事先知道解除这种情况所需的时间,最好写入响应头中的
Retry-After
这个字段再返回给客户端
HTTP报文首部
HTTP1.1 规定了 以下47种首部字段
通用首部 (共9种)
首部字段 | 解释 |
---|---|
1. Cache-Control | 控制缓存的行为 |
2. Connection | 逐跳首部、连接的管理 |
3. Date | 创建报文的日期和时间 |
4. Pragma | 报文指令 |
5. Trailer | 报文末端的首部一览 |
6. Transfer-Encoding | 指定报文主体的传输编码方式 |
7. Upgrade | 升级为其他协议 |
8. Via | 代理服务器的相关信息 |
9. Warning | 错误通知 |
下面我们来看挑几个重要的属性来看下~
- Connection 他有两个作用
- 控制不再转发给代理的首部字段
GET / HTTP/1.1
Upgrade: HTTP/1.1 // 就会把次字段删除后再从代理服务器转发出去
Connection: Upgrade // 不再转发的首部字段名
- 管理持久链接(这个比较常见)
- HTTP/1.1默认连接都是持久连接
Connction: Keep-Alive
- 当服务器想断开的时候,需要指定
Connction: close
- HTTP/1.1默认连接都是持久连接
- Pragme :是
HTTP/1.1
之前版本遗留的字段,仅仅是为了与HTTP/1.0向后兼容而定义Pragm:no-cache
:通用首部字段,在请求头中,表示所有的中间服务器不返回缓存的资源- 可是所有的中间服务器都以
HTTP/1.1
为基准的话,可以直接采用Cache-Control:no-cache
- 所以一般会发送两个字段
Cache-Control:no-cache
Pragm:no-cache
请求报文首部 (共19种)
首部字段 | 解释 |
---|---|
1.Accrpt | 用户代理可处理的媒体类型 |
2.Accrpt-Charset | 优先的字符集 |
3.Accept-Encoding | 优先的内容编码 |
4.Accept-Language | 优先的语言(自然语言) |
5.Authorization | web认证信息 |
6.Expect | 期待服务器的特定行为 |
7.From | 用户的电子邮箱地址 |
8.Host | 请求资源所在服务器 |
9.If-Match | 比较实体标记(ETag) |
10.If-Modified-Since | 比较资源的更新时间 |
11.If-None-Match | 比较实体标记(与If-Match相反) |
12.If-Range | 资源未更新时发送实体Byte的范围请求 |
13.If-Unmodified-Since | 比较资源的更新时间(与If-Modified-Since相反) |
14.Max-Forwards | 最大传输逐跳数 |
15.Proxy-Authorization | 代理服务器要求客户端的认证信息 |
16.Range | 实体的字节范围要求 |
17.Referer | 对请求中URI的原始获取方 |
18.TE | 传输编码的优先级 |
19.User-Agent | HTTP客户端程序的信息 |
- If-Match:只有当
If-Match
字段值跟ETag
值匹配一致时,服务器才会接受请求- 它会告知服务器匹配资源所用的实体标记(ETag)值,这时服务器无法使用弱ETag值
- 仅当两者一致时才会执行请求,否则返回
412 Precondition Failed
的响应 - 还可以使用 * 号指定
If-Match
的字段值,如果这样的话,那么服务器将会忽略ETag的值,只要资源存在就处理请求。
- If-Modified-Since :
- 若资源更新时间确实在此字段指定时间之后的话,则处理该请求,否则返回
304 Not Modified
- 用于确认代理或客户端拥有本地资源的有效性,若想获取资源的更新日期时间的话可以通过确认首部字段
Last-Modified
来确定
- 若资源更新时间确实在此字段指定时间之后的话,则处理该请求,否则返回
- If-None-Match
- 只有在
If-None-Match
的字段值与ETag值不一致时,才可以处理该请求,与前文中提到的If-Match
作用相反
- 只有在
- If-Range
- 他告知服务器若指定的
If-Range
字段值(ETag值或者时间)和请求资源的ETag值或时间一致时,则作为范围请求处理,否则,返回全体资源
- 他告知服务器若指定的
- If-Unmodified-Since
- 指定的请求资源只有在字段值内指定的日期时间之后未发生更新,才会执行这个请求,否则,返回
412 Precondition Failed
状态响应,与If-Modified-Since
作用相反
- 指定的请求资源只有在字段值内指定的日期时间之后未发生更新,才会执行这个请求,否则,返回
- Max-Forwards
- 每次请求转发时数值减一,直到0时返回响应
- 有可能这个请求经过了多台服务器代理转发,如果突然间请求出现了什么问题导致转发失败,而客户端不知道,此时就可以用此属性来定位问题,这个时候我们就可以掌握一个出问题的转发路径,从而方便进一步的排查问题。
- Range:
- 对于只需要获取部分资源的范围请求,
Range
字段可以指定获取资源范围Range: bytes=10001-20000
- 例子中表示请求获取从第10001字节到20000字节的资源
- 服务器处理请求后会返回
206 Partial Content
的响应。无法处理时,则会返回状态码200 OK
的响应及其全部资源
- 对于只需要获取部分资源的范围请求,
响应报文首部 (共9种)
首部字段名 | 解释 |
---|---|
1.Accept-Ranges | 是否接受字节范围请求 |
2.Age | 推算资源创建经过时间 |
3.ETag | 资源的匹配信息 |
4.Location | 令客户端重定向至指定URI |
5.Proxy-Authenticate | 代理服务器对客户端的认证信息 |
6.Retry-After | 对再次发起请求的时机要求 |
7.Server | HTTP服务器的安装信息 |
8.Vary | 代理服务器缓存的管理信息 |
9.WWW-Authenticate | 服务器对客户端的认证信息 |
- Accept-Ranges
Accept-Ranges:bytes
可以处理范围请求Accept-Ranges:none
不可以处理范围请求
- Age
- 可以告知客户端,源服务器多久之前创建了资源,单位是秒
- 若创建该响应的是缓存服务器,则Age值是指缓存后的响应再次发起发起认证到认证完成的时间值。代理创建响应时必须加上首部字段Age
- ETag
它是一种可将资源以字符串形式做唯一标识的方式,服务器会为每份资源分配对应的
ETag
值,资源被更新时,ETag
值也会被更新,并没有统一的算法规则,而是由服务器来分配- 强
ETag
:无论实体发生多么细微的变化都会改变其值 - 弱
ETag
:只用于提示资源是否相同,只有资源发生了根本的改变才会改变ETag值,这时会在字段值最开始加W/
,
ETag:W/"XXX"
- 强
- Location
- 使用该响应字段可以将响应接收方引导至某个与请求的URI位置不同的资源
- 基本上,该字段配合3XX,Redirection的响应,提供重定向的URI
- Vary
首部字段vary可对缓存进行控制,源服务器会向代理服务器传达关于本地缓存使用方法的命令
- 当代理服务器接收到服务器返回包含Vary指定项的响应后,仅对请求中含有相同Vary指定首部字段的请求返回缓存
- 即使对相同资源发起请求,但是由于Vary指定的首部字段不相同,因此必须从源服务器重新获取资源
- 例如下面这个,如果使用的
Accept-Language:en-us
字段的值相同,那么直接从缓存返回响应,否则从源服务器请求资源后再返回响应
实体报文首部 (共10种)
首部字段名 | 解释 |
---|---|
1.Allow | 资源可支持的HTTP方法 |
2.Content-Encoding | 实体主体适用的编码方式 |
3.Content-Language | 实体主体的自然语言 |
4.Content-Length | 实体主体的大小(单位:字节) |
5.Content-Location | 代替对应资源的URI |
6. Content-MD5 | 实体主体的报文摘要 |
7. Content-Range | 实体主体的位置范围 |
8. Content-Type | 实体主体的媒体类型 |
9.Expires | 实体主体过期的日期时间 |
10.Last-Modified | 资源的最后修改日期时间 |
其他字段(cookie等)
- cookie,我们下面单独讲这个
cookie
- 注 : 文中例子中的各种请求,报文,均来自 京东物流官网
- ps:小杰个人挺喜欢JDL的标语的,有速度,更有温度,祝JDL越来越好!
set-cookie
属性 | 解释 |
---|---|
NAME=VALUE | 赋予Cookie的名称和其值(必需项) |
expires = DATE | Cookie的有效期(若不指定则默认为浏览器关闭为止) |
path=PATH | 将服务器上的文件目录作为Cookie的适用对象(若不指定则默认为文档所在的文件目录) |
domin=域名 | 作为Cookie适用对象的域名(若不指定则默认为创建Cookie的服务器域名) |
Secure | 仅在HTTPS安全通信时才会发送Cookie |
HttpOnly | 加以限制,使Cookie不能被JS脚本访问,主要目的是为了防止跨站脚本攻击(Cross Site Scripting,XSS)对cookie的窃取 |
谷歌浏览器控制台查看cookie
- cookie中的
thor
和JSESSIONID
这两个key的后HttpOnly
属性被打上了√,就表明,此key无法被js脚本访问,防止跨站脚本攻击(Cross Site Scripting,XSS)对cookie的窃取
我们来看下再console
控制台输入document.cookie
得出的cookie无法找到这两个key
因为这个属性JSESSIONID
比较重要,存储的是sessionId,这个要是被别人拿到的话,别人就可以冒充我在网站上做某些事情了,像我自己一样请求某些数据了
postman 模拟拿到cookie后发送请求
我把网页上的cookie拿下来,放到postman里测试,发现和我自己在网站上请求数据是一样的
cookie存储的地方,清理缓存到底是清理什么?
清理缓存主要就是清理cookie,抹去自己登陆痕迹以及浏览器中的资源缓存,重新请求网站资源
HTTP 与 HTTPS
HTTP不足
- 通信使用明文(不加密),内容可能会被篡改
- 不验证通信方的身份,因此有可能遭遇伪装
- 无法证明报文的完整性,所以有可能已遭遇篡改
HTTPS结构
HTTPS是身披SSL(Secure Socket Layer)外壳的HTTP
- 在采用SSL后,HTP就拥有了HTTPS的加密、证书和完整性保护这些功能
- 想要了解HTTPS是怎么加密的,得先了解下下面两种提到的加密技术
对称加密原理
- 客户端和服务端约定好用同一把密钥
- 这把密钥可以对数据进行加密/解密
- 客户端和服务端之间的共享密钥的传送问题也是一个问题,如果能够安全传送不被截获的话,那岂不是数据也可以安全的传送到不被截获?鸡生蛋蛋生鸡的问题。
- 图中客户端和服务端传输加密数据的时候,如果双方的共享密钥泄露的被黑客截取到的话,黑客就可以用它来解开这加密的数据,所以对称加密不安全
非对称加密原理(公开密钥加密)
- 一共有两把密钥(是一对),一个公开密钥,一个私人密钥
- 公开密钥加密的数据,只有对应的私钥才可以解密,
- 私钥加密的数据,公钥也可以解密
- 问题就是,从服务端发送给客户端数据时无法保证数据的安全性,因为此时有可能黑客截获到了公钥,对私钥加密的数据进行了解密
- 服务器端为什么不发送用公钥加密的数据?因为客户端没有私钥,无法解密。
混合加密原理
聪明的大佬们用两种加密算法混合了一下
- 客户端一开始向服务端传输的是,用公开密钥加密的共享密钥!
- 这样的话,服务端收到这个加密的数据后用自己的私钥密钥解密后得到的就是共享密钥,以后和客户端交互时都用这个共享密钥就可以啦,因为黑客是无法获得这个共享密钥的,毕竟公开密钥加密的数据,只有对应的私钥才可以解密,而这个私钥一开始就在服务端手里而不在黑客手里
我曾经以为这样就万无一失了,文章也就到此结束了,可以和血包杀手愉快的timi了,可是,你有没有听说过,中间人攻击?
中间人攻击
黑客拦截”用公开加密密钥机密后的共享密钥“后不是解密不了吗,好,那我就不拦截这个了,我拦截第一个请求好吧,我拦截服务端传给你的公开密钥,我拦截到了,我再给你个假的,(像极了《让子弹飞》中,张麻子与马邦德的关系,出任鹅城县长)。从根上就伪装成你,以后就等于我是个中间人(中转站),所有的请求,数据都要经过我,那我就可以记录下来其中你的敏感数据,可怕。
- 其实中间人攻击还要有好多种,以后有机会写一写,我们先大概了解下是什么意思就好~
数字证书
- 所以现在问题又到了这里,我无法确保这个公钥是服务端发给我的,还是中间人发给我的?可这世上没有用钱解决不了的问题,虽然我确保不了,可是有人可以确保,就是得花钱,我们可以使用由数字证书认证机构(CA)和其他相关机构颁发的公开密钥证书
- 数字证书认证机构处于客户端和服务器双方都信赖的第三方机构的立场上。有兴趣的同学可以自行去了解下~
- 所以HTTPS靠非对称性加密及数字证书保证了安全性
写在最后
总结
- 此文章从HTTP报文结构开始,到HTTP首部,到返回状态码,到cookie,再延伸到HTTPS加密方式,每一部分都进行了详细的介绍,希望对大家有用!
往期精彩推荐
- 同学,二叉树的各种遍历方式,我都帮你总结了,附有队列堆栈图解(巩固基础,强烈建议收藏)
- 京东面试官问我:“聊聊MySql事务,MVCC?(好文,建议收藏)”
- 你好,我叫AQS(系列一:加锁)
- 京东这道面试题你会吗?
- ?线程池为什么可以复用,我是蒙圈了。。。
- 学会了volatile,你变心了,我看到了
絮絮叨叨
- 如果大家觉得这篇文章对自己有一点点帮助的话,欢迎关注此公众号 java小杰要加油
- 原创不易,实需鼓励
若文章有误欢迎指出,靓仔靓女们,我们下篇文章见,扫一扫,开启我们的故事