Android网络知识梳理

678 阅读36分钟

本文为过往笔记整理, 在此只作记录,不做严谨的技术分享。

一篇文章看明白 TCP/IP,TCP,UDP,IP,Socket 之间的关系
Socket学习网络基础准备
看完这篇HTTP,跟面试官扯皮就没问题了
HTTP 的15个常见知识点复习

协议族

不同的硬件、操作系统之间的通信,所有的这一切都需要一种规则,而我们就把这种规则称为协议(protocol)。

TCP/IP 是各类协议族的总称,比如:TCP,UDP,IP,FTP,HTTP,ICMP,SMTP 等都属于协议族内的协议。

TCP/IP

TCP/IP 网络协议知识点总结

TCP/IP模型:

  • 开放式系统互联模型(英语:Open System Interconnection Model,缩写:OSI;简称为OSI模型)是一种概念模型,由国际标准化组织提出,一个试图使各种计算机在世界范围内互连为网络的标准框架。

image-20211109145945854

image-20210415150348897

image-20210415150404434

  • 物理层(Physical):设备之间的数据通信提供传输媒体,为数据传输提供可靠的 环境。 可以理解为网络传输的物理媒体部分,比如网卡,网线,集线器,中继器,调制解调器等! 在这一层,数据还没有被组织,仅作为原始的位流或电气电压处理,这一层的单位是:bit比特
  • 链路层(Datalink):该层作用包括:物理地址寻址,数据的成帧,流量控制,数据检错以及重发等! 媒体是 长期的,连接是有生存期的。在连接生存期内,收发两端可以进行一次或多次数据通信。 每次通信都要经过建立通信联络和拆除通信联络两过程!该层的设备有:网卡,网桥,网路交换机,另外该层的单位为:
  • 网络层(Network):将网络地址翻译成对应的物理地址,并决定如何将数据从发 送方路由到接收方; 所谓的路由与寻径:一台终端可能需要与多台终端通信,这样就产生的了 把任意两台终端设备数据链接起来的问题!简单点说就是:建立网络连接和为上层提供服务! 该层的设备有:路由!该层的单位为:数据包,另外IP协议就在这一层!
  • 传输层(Transport):向上面的应用层提供通信服务,面向通信部分的最高层,也是 用户功能中的最低层。 接收会话层数据,在必要时将数据进行分割,并将这些数据交给网络 层,并且保证这些数据段有效的到达对端!所以这层的单位是:数据段;而这层有两个很重要 的协议就是:TCP传输控制协议UDP用户数据报协议
  • 会话层(Session):负责在网络中的两节点之间建立、维持和终止通信。 建立通信链接, 保持会话过程通信链接的畅通,同步两个节点之间的对话,决定通信是否被中断以及通信中断时 决定从何处重新发送,即不同机器上的用户之间会话的建立及管理!
  • 表示层(Presentation):对来自应用层的命令和数据进行解释,对各种语法赋予相应 的含义,并按照一定的格式传送给会话层。其主要功能是"处理用户信息的表示问题,如编码、 数据格式转换和加密解密,压缩解压缩"等
  • 应用层(Application):OSI参考模型的最高层,为用户的应用程序提供网络服务。 它在其他6层工作的基础上,负责完成网络中应用程序与网络操作系统之间的联系,建立与结束使用者之间的联系,并完成网络用户提出的各种网络服务及应用所需的监督、管理和服务等各种协议。此外,该层还负责协调各个应用程序间的工作。应用层为用户提供的服务和协议有:文件服务、目录服务、文件传输服务(FTP)、远程登录服务(Telnet)、电子邮件服务(E-mail)、打印服务、安全服务、网络管理服务、数据库服务等

TCP

淘宝二面,面试官居然把 TCP 三次握手问的这么详细
彻底明白TCP的三次握手与四次挥手
TCP/IP 网络协议知识点总结
硬不硬你说了算!近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题

面向链接、可靠(知道是否收到消息,有重传机制)

TCP(Transmission Control Protocol 传输控制协议)是一种**面向连接的、可靠的基于字节流**(提供数据的分包、组装、排序等功能)的传输层通信协议,由 IETF 的RFC 793定义。

TCP 是面向连接的、可靠的流协议。流就是指不间断的数据结构,可以把它想象成排水管中的水流。TCP 为了保证报文传输的可靠,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端对已成功收到的字节发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据(假设丢失了)将会被重传。

640

  • TCP 头结构:来源端口、目的端口、序号、确认序号、SYN/ACK 等状态位、窗口大小、校验和、紧急指针
  • 特点:面向连接、面向字节流、可靠、有序、速度慢、较重量,流量控制、拥塞控制
  • 适用场景:文件传输、浏览器等
  • 应用:HTTP、HTTPS、RTMP、FTP、SMTP、POP3

链接建立(三次握手)

  • SYN表示建立连接

  • FIN表示关闭连接

  • ACK表示响应

  • PSH表示有 DATA数据传输

  • RST表示连接重置

img

image-20211019153419912

TCP 三次握手,其实就是 TCP 应用在发送数据前,通过 TCP 协议跟通信对方协商好连接信息,建立起TCP的连接关系。

TCP 连接并非是在通信设备两端之间建立信号隧道,其本质上就是双方各自维护所需的状态机。握手的主要目的就是为了确认双方的接收和发送能力是否正常,初始序列号,交换窗口大小以及 MSS 等信息。

三次握手:

  1. 客户端发送 SYN 报文和客户端包起始序列号(x),并进入 SYN_SENT 状态,等待服务器的确认
  2. 服务器收到 SYN 报文后,给客户端发送 SYN 报文和服务端包起始序列号(y)。 以及ACK报文和客户端包其实序列号+1(表示对客户端包起始序列号达成共识),此时服务器进入 SYN_RCVD 状态;
  3. 客户端收到 SYN 和 ACK 报文后,向服务器发送ACK报文和服务端包起始序列号+1(表示对服务端包其实序列号达成共识),客户端进入 ESTABLISHED[/ɪˈstæblɪʃt/] 状态。 待服务器收到客户端发送的 ACK 包也会进入 ESTABLISHED 状态,完成三次握手。
为什么不两次握手?
  • 不能保证验证的完整性:如果两次,服务端不知道客户端是否收到自己发出的消息
  • 防止重复链接:如果客户端发出了新旧两个SYN包,旧的SYN包先到达服务器,两次握手的话此时服务器就会立刻建立与旧SYN包的链接,造成异常。 如果是三次握手的话,客户端就能比对出SYN是旧包,从而不发出第三次握手,不与服务端建立链接

所以,三次握手有足够的上下文信息来判断是否建立链接。

三次握手可以携带数据么?

第一、二次不可以,第三次可以

假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据,疯狂着重复发 SYN 报文,这会让服务器花费大量的内存空间来缓存这些报文,这样服务器就更容易被攻击了。 对于第三次握手,此时客户端已经处于连接状态,他已经知道服务器的接收、发送能力是正常的了,所以可以携带数据是情理之中。

链接终止(四次挥手)

img

四次挥手:

  1. 客户端发送 FIN 包和客户端包序列号,客户端进入 FIN_WAIT_1 状态,表示客户端没有数据要发送了
    • TCP 规定,即使 FIN 包不携带数据,也要消耗一个序号
  2. 服务端发送 ACK包和客户端序列号+1,并带上自己的序号 seq=v,服务器端进入 CLOSE_WAIT 状态,表示对客户端没有数据要发送了达成共识
    • 这个时候客户端已经没有数据要发送了,不过服务器端有数据发送的话,客户端依然需要接收。
    • 客户端接收到服务器端发送的 ACK 后,进入了 FIN_WAIT_2 状态。
  3. 服务器端数据发送完毕后,向客户端发送 FIN 包和服务端包序列号,服务器此时进入了 LAST_ACK 状态,表示服务端没有数据要发送了
  4. 客户端发送ACK包和服务端包序列号+1,此时客户端就进入了 TIME_WAIT 状态,表示对服务端没有数据要发送了达成共识
    • 注意此时 TCP 连接还没有释放,必须经过 2*MSL 后,才进入 CLOSED 状态。
    • 而服务器端收到客户端的确认包 ACK 后就进入了 CLOSED 状态,可以看出服务器端结束 TCP 连接的时间要比客户端早一些
为什么握手只需要3次,关闭需要4次?

建立连接的时候, 服务器收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。 而关闭连接时,服务器收到对方关闭链接的FIN报文时,服务端有可能不需要发送数据了,也有可能还有数据要发送。所以服务端只能先发送ACK确认包,待没有数据要发送了再发送FIN报文给客户端来表示服务端没有数据可以关闭了。因此,ACK和FIN一般都会分开发送,从而导致多了一次。

为什么需要等待2MSL?
  1. 确保客户端发送的ACK(即第4次挥手)到达服务端(让服务端知道客户端收到了第三次挥手)
    • MSL是报文在网络中最大的生存时间,在客户端发送ACK包后是有可能到达不了服务端的,MSL时间内服务端收不到ACK包会重新发送FIN包
    • 所以,ACK到达服务器 + 服务器重传FIN包的最大时间2MSL内,客户端在等待是否会收到服务端重传的FIN包,如果超过2MSL没有收到,则证明服务端已经收到了客户端传的ACK包,此时客户端可以断开链接了
  2. 经过 2MSL 时间,就可以使本连接持续的时间内所产生的所有报文都从网络中消失,使下一个新的连接中不会出现旧的连接请求报文

UDP

无链接、不可靠(不知道是否收到消息,无重传机制)

UDP (User Datagram Protocol , 用户数据报协议),是 OSI参考模型中一种无连接不可靠的传输层协议,在网络中它与 TCP 协议一样用于处理数据包。

UDP 有不提供数据包分组、组装和不能对数据包进行排序的缺点,当报文发送之后,是无法得知其是否安全完整到达的

640 (1)

  • UDP 头结构:来源端口、目的端口、长度域、校验和
  • 特点:无连接、面向报文、不可靠、无序、速度快、轻量、实时性高(无队首阻塞)
  • 适用场景:适用于一对多、即时通讯、视频通话等
  • 应用:DHCP、DNS、QUCI、VXLAN、GTP-U、TFTP、SNMP

TCP、UDP 区别

特点TCPUDP
连接性面向连接无连接
可靠性可靠(双向、重传)不可靠(单向)
传输效率
场景访问API游戏、直播(不需要知道数据是否丢失,只需要知道实时信息)

Socket

Android基于TCP的Socket编程实现
进程通信之 Socket
基于TCP/UDP协议的Socket通信

两个进程进行通讯最基本的前提是能唯一标识一个进程,在本地进程通讯中可以使用 PID 来唯一标识一个进程,但网络中两个进程 PID 冲突几率很大,IP 层的 ip 地址可以唯一标示主机,再结合 TCP 端口号就可以唯一标示网络上某个主机的进程了。 我们经常把 Socket 翻译为套接字,Socket 是在应用层和传输层之间的一个抽象层它把 TCP、UDP 等复杂的操作抽象为几个简单的接口供应用层调用从而实现进程在网络中通信

img

img

img

img

WebSocket

WebSocket 教程
刨根问底HTTP和WebSocket协议

服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,双向通信协议,属于服务器推送技术的一种。

其他特点包括:

(1)建立在 TCP协议 之上

(2)与 HTTP 协议有着良好的兼容性。默认端口80和443,握手阶段采用 HTTP 协议

(3)数据格式比较轻量,性能开销小,通信高效

(4)可以发送文本,也可以发送二进制数据

(5)没有同源限制,客户端可以与任意服务器通信

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

长连接

为什么要⻓连接? 因为移动⽹络并不在 Internet 中,⽽是在运营商的内⽹,并不具有真正的公⽹ IP,因此当某个 TCP 连接在⼀段时间不通信之后,⽹关会出于⽹络性能考虑⽽关闭这条 TCP 连接和公⽹的连接通道,导致 这个 TCP 端⼝不再能收到外部通信消息,即 TCP 连接被动关闭。

⻓连接的实现⽅式: ⼼跳。即在⼀定间隔时间内,使⽤ TCP 连接发送超短⽆意义消息来让⽹关不能将⾃⼰定义为「空闲连 接」,从⽽防⽌⽹关将⾃⼰的连接关闭。

HTTP

是一种网络传输协议,应用层。

  • Hypertext Transfer Protocol,超文本传输协议。
  • 超文本,「扩展型文本」,
  • 超链接、超文本,“超”指的是:扩展。

请求/响应报文

image-20210413232712262 image-20210413232742341

请求行

method

根据 HTTP 标准,HTTP 请求可以使用多种请求方法。 HTTP1.0 定义了三种请 求方法:GET, POST 和 HEAD 方法。HTTP1.1 新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE

  • GET 请求指定的页面信息,并返回实体主体。
  • HEAD 类似于 get 请求,只不过返回的响应中没有具体的内容,用于获取报头
  • POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。
  • PUT 从客户端向服务器传送的数据取代指定的文档的内容。
  • DELETE 请求服务器删除指定的页面。 CONNECT HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
  • OPTIONS 允许客户端查看服务器的性能。
  • TRACE 回显服务器收到的请求,主要用于测试或诊断。
POST
  • 用于增加、修改
  • 发送给服务器的内容写在body里
    • body内容: name=varmin&sex=1
GET
  • 获取数据
  • 对服务器内容不进行修改
  • 不发送body
HEAD
  • 和GET使用方法相同
  • 和GET区别:返回的响应中没有body
PUT
  • 修改数据
  • 发送给服务器的内容写在body
    • body内容: name=varmin&sex=1
DELETE
  • 删除资源
  • 不发送body
幂等

GET、PUT、DELETE:执行一次或多次对服务器的资源操作结果是一样的

状态行

status code

对响应结果做出类型化描述

1xx: 临时性消息
  • 100: 继续发送
  • 101: 正在切换协议
2xx: 成功
  • 200:ok
  • 201:创建成功
3xx: 重定向
  • 301: 永久
  • 302: 暂时移动
  • 304: 内容未改变
4xx:客户端错误
  • 400:客户端请求错误
  • 401:认证失败
  • 403:被禁止
  • 404:找不到内容
5xx:服务器错误
  • 500: 服务器内部错误

Headers

Http的meta data(原数据) ,数据的数据,描述、配置数据的数据。

例如:各种headers,是请求、响应报文数据的原数据。

Host

不是寻址的,Host是用来在目标服务器上定位子服务器的;

  1. 在发出这个请求之前已经通过DNS找到服务器的IP地址了;
  2. 在Headers中带上 Host: api.github.com是为了定位子服务器的;
    • 多个域名可能对应同一个服务器IP地址
    • 带上host,作为一个标识,就可以知道要找的是哪个子服务器了
Accept

客户端能接收的数据类型,如text/html

Accept-Charset

客户端接受的字符集,如utf-8

Accept-Encoding

客户端接受的压缩编码类型,如gzip

Content-Encoding

压缩类型。如gzip

Content-lenght

内容的长度(字节)

Content-type

指定body的类型

text/html

body中返回html文本

x-www-form-urlcoded

web页面纯文本表单的提交方式

application/json

响应或请求,消息体为body

multitype/form-data

web页面含有二进制文件时的提交方式

image/jpeg

请求中提交二进制数据

......

Location

重定向目标URL

User-Agent

用户代理,即是谁真正发送请求、接收响应的。

Range

按范围接收数据

  • Range: bytes=-:请求报文中出现,表示要取哪段范围的数据;
  • Accept-Range: bytes : 响应报文中出现,表示服务器支持按字节范围来取数据;
  • Content_Range:-/total: 响应报文出现,表示发送的是哪段范围的数据。

多用于断点续传、多线程下载

Cache
  • Cache-control: private, max-age=0, no-cache...
  • Expires、Age、pragma...
Authorization

授权信息

Cookie/Set-Cookie

发送Cookie/设置Cookie

Body

  • name='varmin'&age=22
  • {name:'varmin', age:22}
  • --boundary
  • chunked(分块传输 Chunked Transfer Encording)

HTTP2

HTTP/2.0 原理!与 1.x 相比,到底优化了什么?
juejin.cn/post/700183…

  • 二进制分帧
  • 多路复用
  • 头部压缩
  • 请求优先级
  • 服务器推送

HTTP3

img

HTTPS

img img

HTTP 是明文传输,很容易被窃听或篡改信息。 HTTPS(Hyper Text Transfer Protocol over Secure Socket Layer),默认端口号443(HTTP默认80)。不是一种新协议,只是在 HTTP 的基础上通过身份认证传输加密保证了传输过程的安全性。HTTPS 在 HTTP 的基础上增加了 SSL 层,后来以SSL为基础制定了TLS(Transport Layer Secure),也就是说 HTTPS = HTTP + SSL/TLS

工作原理:在客户端和服务端通过非对称加密协商出一套对称加密的秘钥,发送接收消息时加密、解密,达到内容的加密传输。(为什么不直接用非对称加密/解密:计算复杂,影响网络性能)

工作流程

HTTPS流程详解
清晰图解HTTPS原理

img

Client Hello

  • 可以接受的SSL/TLS版本
  • 支持的加密算法套件(Cipher Suite):可以接受的对称/非对称加密、hash算法
  • 客户端随机数 Random1

Server Hello

  • 确认后的SSL/TLS
  • 确认后的Cipher Suite
  • 客户端随机数Random1
  • 服务端随机数Random2
Server 发送证书
  • 服务器地址、名称等信息
  • 证书公钥
  • 证书签名:验证证书内容
    • 证书机构公钥
    • 证书机构信息
      • 证书机构的签发方
      • 证书机构的签发方公钥在系统里:验证证书机构
image-20211108114103166

Client 校验证书、发送Pre-master Secret

  • 非对称加密:客户端生成Pre-Master Secret随机数,并使用证书公钥进行加密,发送给服务器

  • 关键性的一步:此后,客户端、服务端都有Random1、Random2、Pre-Mater Secret,这3个随机数生成Master Secret,避免了对称加密秘药的传输。同时,会根据Mater Secret生成6个秘钥(P1~P6)

    • 客户端加密秘钥:客户端发送消息(对称加密,服务端也有该秘钥,可解)
    • 服务端加密秘钥:服务端发送消息(对称加密,客户端也有该秘钥,可解)
    • 客户端 MAC Secret:验证
    • 服务端 MAC Secret:验证
    • ......

Client 握手结束通知

  • Client 发送changeCipherSpec提示服务器此后使用pre-master secret产生的密钥加密通信
  • Client 发送FIN报文,表示结束

Server 握手结束通知

  • Server 发送changeCipherSpec报文
  • Server 发送FIN报文,表示结束

证书

CA认证

img

CA 的全称是 Certificate Authority,证书认证机构。必须让 CA 颁布具有认证过的公钥,才能解决公钥的信任问题。全世界具有认证的 CA 就几家,分别颁布了 DV、OV、EV 三种,区别在于可信程度。

  • DV 是最低的,只是域名级别的可信
  • EV 是最高的,经过了法律和审计的严格核查,可以证明网站拥有者的身份(在浏览器地址栏会显示出公司的名字,例如 Apple、GitHub 的网站)

不同的信任等级的机构一起形成了层级关系

img

自签名证书

没有通过受信任的证书颁发机构,自己给自己颁发的证书

Android信任Https自签名证书详细教程
Android 实现HTTPS自签名证书
通过 HTTPS 和 SSL 确保安全-dev

Android对于Https的支持在系统层面上已经帮我们封装的很好了,系统会内置合法ca机构的根证书,只要服务器证书是由这些机构或者其中间机构签发的即可。系统会自动做安全校验,我们不需要做任何事情,直接请求https就可以。

但是如果服务器证书是自签名的,就需要我们对请求逻辑做一定的改造,不然你发起的所有请求都会失败,因为系统在做ssl证书校验时会认为这是一个非法请求直接阻断掉。此时就需要自己实现逻辑去验证证书的合法性了。

需要自己实现证书验证的情况:

  • 对安全性要求比较高
  • 只用于内网的https
  • 证书信息不全,缺乏证书机构信息
  • 手机操作系统太久,没有安装最新加入的证书
信任所有证书(有风险)

该方法有风险,只可在debug包或测试阶段使用

/**
* 创建信任所有证书的OkHttpClient
* @return
*/
public static OkHttpClient createTrushAllClient(){
   return createSSLClient(createTrustAllTrustManager());
}

/**
* 创建信任所有证书的TrustManager
* @return
*/
private static X509TrustManager createTrustAllTrustManager() {
	return new X509TrustManager() {
	    @Override
	    public void checkClientTrusted(X509Certificate[] chain, String authType) 
            throws CertificateException {
	
	    }
	
	    @Override
	    public void checkServerTrusted(X509Certificate[] chain, String authType) 
            throws CertificateException {
	
	    }
	
	    @Override
	    public X509Certificate[] getAcceptedIssuers() {
	        return new X509Certificate[0];
	    }
   };
}

private static OkHttpClient createSSLClient(X509TrustManager x509TrustManager){
	OkHttpClient.Builder builder = new OkHttpClient.Builder()
	        .connectTimeout(60, TimeUnit.SECONDS)
			.sslSocketFactory(createSSLSocketFactory(x509TrustManager),x509TrustManager)
	        .hostnameVerifier(new TrustAllHostnameVerifier());
	return builder.build();
}

private static SSLSocketFactory createSSLSocketFactory(TrustManager trustManager) {
    SSLSocketFactory ssfFactory = null;
    try {
        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, new TrustManager[]{trustManager}, new SecureRandom());
        ssfFactory = sc.getSocketFactory();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return ssfFactory;
}

//实现信任所有域名的HostnameVerifier接口
private static class TrustAllHostnameVerifier implements HostnameVerifier {
	@Override
	public boolean verify(String hostname, SSLSession session) {
	    //域名校验,默认都通过
	    return true;
	}
}

信任指定证书
/**
 * 创建信任指定证书的OkHttpClient(可以用于信任自签名证书)
 * @return
 */
public static OkHttpClient createTrustCustomClient(InputStream inputStream){
    return createSSLClient(createTrustCustomTrustManager(inputStream));
}
/**
 * 创建只信任指定证书的TrustManager
 * @param inputStream:证书输入流
 * @return
 */
@Nullable
private static X509TrustManager createTrustCustomTrustManager(InputStream inputStream) {
    try {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null);

        Certificate certificate = certificateFactory.generateCertificate(inputStream);
        //将证书放入keystore中
        String certificateAlias = "ca";
        keyStore.setCertificateEntry(certificateAlias, certificate);
        if (inputStream != null) {
            inputStream.close();
        }

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.
                getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keyStore);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

        if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
            throw new IllegalStateException("Unexpected default trust managers:"
                    + Arrays.toString(trustManagers));
        }
        return (X509TrustManager) trustManagers[0];
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}
private static OkHttpClient createSSLClient(X509TrustManager x509TrustManager){
    OkHttpClient.Builder builder = new OkHttpClient.Builder()
            .connectTimeout(60, TimeUnit.SECONDS)
            .sslSocketFactory(createSSLSocketFactory(x509TrustManager),x509TrustManager)
            .hostnameVerifier(new TrustAllHostnameVerifier());
    return builder.build();
}

private static SSLSocketFactory createSSLSocketFactory(TrustManager trustManager) {
    SSLSocketFactory ssfFactory = null;
    try {
        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, new TrustManager[]{trustManager}, new SecureRandom());
        ssfFactory = sc.getSocketFactory();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return ssfFactory;
}

//实现信任所有域名的HostnameVerifier接口
private static class TrustAllHostnameVerifier implements HostnameVerifier {
    @Override
    public boolean verify(String hostname, SSLSession session) {
        //域名校验,默认都通过
        return true;
    }
}

抓包

Android 高版本 HTTPS 抓包解决方案及问题分析!
在Android手机上对https请求进行抓包
通过 HTTPS 和 SSL 确保安全-dev

HTTPS 使用了 SSL 加密协议,是一种非常安全的机制,目前并没有方法直接对这个协议进行攻击

中间人攻击

一般都是在建立 SSL 连接时(Client Hello阶段),利用中间人拦截客户端的请求,对通信内容进行拦截和篡改。中间人攻击是指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。 img

image-20211124104001009

MITM Server 伪装成真正的 Server

MITM Server 要成为真正的 Server,必须能够给client指定访问的域名签发公钥证书,且公钥证书能够通过系统的安全校验,并且自己持有与公钥向匹配的私钥。

MITM Server 的处理方式是从第一个 SSL/TLS 握手包 Client Hello 中提取出域名,例如:www.baidu.com,利用应用内置的 CA 证书创建 www.baidu.com 域名的公钥证书和私钥。

创建的公钥证书在 SSL/TLS 握手的过程中发给 Client,Client 收到公钥证书后会由系统会对此证书进行校验,判断是否是百度公司持有的证书,但很明显这个证书是抓包工具伪造的。为了能够让系统校验公钥证书时认为证书是真实有效的,我们需要将抓包应用内置的 CA 证书手动安装到系统中,作为真正的证书发行商(CA),即洗白。这就是为什么,HTTPS 抓包一定要先安装 CA 证书。

安装CA证书到系统

抓包应用内置的 CA 证书要洗白,必须安装到系统中

Android 系统将 CA 证书分为两种:用户 CA 证书和**系统 CA** 证书。 对于非 Root 的 Android 设备,用户只能安装用户 CA 证书。 无论是系统 CA 证书还是用户 CA 证书,都可以在设置 → 系统安全 → 加密与凭据 → 信任的凭据中查看。

image-20211124105350754

7.0用户CA限制

Android 从 7.0 开始,系统不再信任用户 CA 证书(应用 targetSdkVersion >= 24 时生效,如果 targetSdkVersion <24 即使系统是 7.0 + 依然会信任)。

也就是说即使安装了用户 CA 证书,在 Android 7.0 + 的机器上,targetSdkVersion>= 24 的应用的 HTTPS 包就抓不到了。 比如上面的例子,抓包工具用内置的 CA 证书,创建了 www.baidu.com 域名的公钥证书发给 Client,系统校验此证书时发现是用户 CA 证书签发的,那么就不再信任该公钥了。

绕过方法:

  • AndroidManifest 中配置 networkSecurityConfig

表示App 信任用户 CA 证书,让系统对用户 CA 证书的校验给予通过

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:networkSecurityConfig="@xml/network_security_config">
    ...
    </application>
</manifest>


<!--network_security_config.xml-->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <!-- 仅在debug模式下 -->
        <trust-anchors>
            <!--7.0后默认没有该配置:信任用户添加的证书 -->
            <certificates src="user" />
            <!--默认配置,可不写 -->
            <certificates src="system" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>
 
  • 调低 targetSdkVersion < 24
  • root手机,安装到系统CA目录

MITM Client 伪装成真正的 Client

由于服务器并不会校验 Client(绝大部分情况),所以这个问题一般不会存在。 比如 Server 一般不会关心 Client 到底是 Chrome 浏览器还是 IE 浏览器,是 Android App 还是 iOS App。当然,Server 也是可以校验 Client 的。

需要抓包的情况:

  • 测试阶段抓包

RESTful

Representational State Transfer,架构风格(不是标准),使用正确的格式请求和响应

  1. 使用资源格式来定义URL

  2. 规范使用method来定义网络请求操作 (各种GET、POST等方法,是后台定好的前端遵循。而不是前端来确定使用什么方式,但现在存在的问题是,后台并不一定会按照规则设计接口)

  3. 规范使用status code来表示响应状态

编码

把数据从一种形式转换为另一种形式

Base64

算法

将原数据每6位对应成Base 64索引表中的一个字符(8位),编排成一个字符串。

image-20210721222626981 image-20210721222700128

用途

  1. 将⼆进制数据扩充了储存方式传输途径:例如可以把数据保存到⽂本⽂件、可以通过聊天对话框或 短信形式发送⼆进制数据、可以在 URL 中加⼊简单的⼆进制数据
  2. 防偷窥:普通的字符串在经过 Base64 编码后的结果会变得⾁眼不可读,因此可以适⽤于⼀定条件下的防 偷窥(较少⽤)

缺点

因为⾃身原理(6 位变 8 位),因此每次 Base64 编码之后,数据都会增⼤约 1/3,会影响存储和传输性能。

传输图片更安全⾼效?

不。

  • Base64 会导致数据增⼤ 1/3,降低⽹络性能,增⼤⽤户流量开销,是画蛇添⾜的⼿段。
  • Base64 对图⽚进⾏编码的目的在于:有时需要使⽤⽂本形式来传输图⽚(例如:使用GET请求提交图片、在只能发送文本的环境中)。除此之外,完全没必要使 ⽤ Base64 对图⽚进⾏额外处理。

URL encoding

在 URL 的字符串中,对⼀些不⽤于特殊⽤途的保留字符,使⽤百分号 "%" 为前缀进⾏单独编码,以避 免出现解析错误。

例如,要在 hencoder.com/users 后⾯添加查询字符串,查询 name 为「隐匿&伟⼤」的⽤ 户,如果直接写成 hencoder.com/user/?name=… ,"&" 符号就会被解析为分隔符号,因此需要对它进⾏转码,转码后的 URL 为 hencoder.com/user/?name=…

压缩、解压缩

减少数据占用的空间

  • 压缩:将数据使⽤更具有存储优势的编码算法进⾏编码。
  • 解压缩:将压缩数据解码还原成原来的形式,以⽅便使⽤。

例如,将下⾯⽂本内容压缩:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

使⽤某种算法压缩后的数据为:

compress:a:1062;b:105

常⻅压缩算法 DEFLATE、JPEG、MP3 等。

Hash

把任意数据转换成指定⼤⼩范围(通常很⼩,例如 256 字节以内)的数据。

相当于从数据中提取出摘要信息,主要用于数字指纹,验证唯一性。

从⽹络上下载⽂件后,通过⽐对⽂件的 Hash 值(例如 MD5、SHA1),可以确认下载的⽂件是否有 损坏。如果下载的⽂件 Hash 值和⽂件提供⽅给出的 Hash 值⼀致,则证明下载的⽂件是完好⽆损 的

算法:MD5、SHA1、SHA256等

MD5

/**
 * MD5加密工具类
 */
public class Md5EncryptionHelper {

    /**
     * 获取MD5字符串
     */
    public static String getMD5(String content) {
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            digest.update(content.getBytes());
            return getHashString(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String getHashString(MessageDigest digest) {
        StringBuilder builder = new StringBuilder();
        for (byte b : digest.digest()) {
            builder.append(Integer.toHexString((b >> 4) & 0xf));
            builder.append(Integer.toHexString(b & 0xf));
        }
        return builder.toString();
    }
  
  
    private static final String SALT = "0fdfa5e5a88bebae640a5d88e7c84708";
    /**
     * 获取加盐的MD5字符串
     */
    public static String getMD5WithSalt(String content) {
        return getMD5(getMD5(content) + SALT);
    }
}
盐动态生成

MD5自身是不可逆,但是现在网络上的MD5数据库的数据量已经非常庞大,大部分常用密码都可以通过碰撞的方法给暴力破解出来,网上比较有名的有彩虹表都能很好的破解常用MD5加密,所以一般通过加盐的方式来提高数据的安全;

public class PasswordUtil {
	public static void main(String[] args) {
		String password = generate("admin");
		System.out.println(verify("admin", password));
	}
  
	/**
	 * 生成含有随机盐的密码
	 */
	public static String generate(String password) {
		Random r = new Random();
		StringBuilder sb = new StringBuilder(16);
		sb.append(r.nextInt(99999999)).append(r.nextInt(99999999));
		int len = sb.length();
		if (len < 16) {
			for (int i = 0; i < 16 - len; i++) {
				sb.append("0");
			}
		}
		String salt = sb.toString();
		password = md5Hex(password + salt);
		char[] cs = new char[48];
		for (int i = 0; i < 48; i += 3) {
			cs[i] = password.charAt(i / 3 * 2);
			char c = salt.charAt(i / 3);
			cs[i + 1] = c;
			cs[i + 2] = password.charAt(i / 3 * 2 + 1);
		}
		return new String(cs);
	}

 
	/**
	 * 获取十六进制字符串形式的MD5摘要
	 */
	public static String md5Hex(String src) {
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			byte[] bs = md5.digest(src.getBytes());
			return new String(new Hex().encode(bs));
		} catch (Exception e) {
			return null;
		}
	}
  
   
	/**
	 * 校验密码是否正确
	 */
	public static boolean verify(String password, String md5) {
		char[] cs1 = new char[32];
		char[] cs2 = new char[16];
		for (int i = 0; i < 48; i += 3) {
			cs1[i / 3 * 2] = md5.charAt(i);
			cs1[i / 3 * 2 + 1] = md5.charAt(i + 2);
			cs2[i / 3] = md5.charAt(i + 1);
		}
		String salt = new String(cs2);
		return md5Hex(password + salt).equals(new String(cs1));
	}
}

加密

可逆与不可逆加密算法,对称与非对称加密算法

加密:加密后的消息是完整的,解密可得到原始数据

摘要:得到的消息是不完整的,不可得到原始数据

可逆:加密、解密

不可逆:加密后不可解密,如MD5、SHA、HMAC

  • MD5:摘要算法 / 散列算法 / 哈希算法,不算加密算法
    • 摘要:将任意长度的二进制字符串映射为固定长度的小型二进制字符串
    • 不可逆
    • 作用:一致性检验、数字签名
  • base64:
    • Base64编码解码具有不可读性,即所编码的数据不会被人用肉眼所看出意义
    • 只是一种编码方式,不算加密方式
  • 对称加密
    • 文件加密和解密使用相同的密钥,即加密密钥也可以用作解密密钥
    • 优点:算法公开、计算量小、加密速度快
    • 缺点:没有“非对称加密”安全
    • 用途:一般用于保存用户手机号、身份证等敏感但能解密的信息。
  • 非对称加密
    • 两个密钥:公开密钥(publickey)和私有密钥,公有密钥加密,私有密钥解密
    • 优点:安全性更好
    • 缺点:加密/解密时间长、速度慢,只适合对少量数据加密
    • 用途:签名和认证

对称加密

image-20210719222759005

通信双⽅使⽤同⼀个密钥,使⽤加密算法配合上密钥来加密,解密时使⽤加密过程的完全逆过程,配合密钥来进⾏解密。

简化模型即古典密码学中替换式加密的模型:

  • 对⽂字进⾏规则化替换来加密,对密⽂进⾏逆向的规则化替换来解密
  • 例如:谍战片里,将文字信息(元数据)通过一本书(秘钥)转换为页码行号(算法)加密,将密文的页码和行号对应同一本书进行解密

经典算法

DES(56 位密钥,密钥太短⽽逐渐被弃⽤)、AES(128 位、192 位、256 位密钥,现在最流⾏)

破解思路

  • 拿到⼀组或多组原⽂-密⽂对

  • 设法找到⼀个密钥,这个密钥可以将这些原⽂-密⽂对中的原⽂加密为密⽂,以及将密⽂解密为原⽂的组合,即为成功破解

反破解

⼀种优秀的对称加密算法的标准是,让破解者找不到⽐穷举法(暴⼒破解法)更有效的破解⼿段,并且穷举法的破解时间⾜够⻓(例如数千年)。

缺点

密钥泄露:不能在不安全⽹络上传输密钥,⼀旦密钥泄露则加密通信失败

非对称加密

image-20210719223141728

发送方:使⽤对方的**公钥**对数据进⾏加密得到密⽂

接收方:使⽤自己的**私钥**对数据进⾏解密得到原数据。

⾮对称加密使⽤的是复杂的数学技巧,在古典密码学中没有对应的原型。

使⽤⾮对称加密通信,可以在不可信⽹络上将双⽅的公钥传给对⽅,然后在发消息前分别对消息使⽤对⽅的公钥来加密使⽤⾃⼰的私钥来签名,做到不可信⽹络上的可靠密钥传播及加密通信。

公钥加密、私钥解密;私钥签名、公钥验签

  • A通过公钥B加密 ---> B通过私钥B解密
  • A通过私钥A签名 ---> B通过公钥A验签

image-20210719223212146

签名与验证

私钥签名、公钥验证

image-20210719223240198

image-20210719223307305

加密与签名

  • 下图右边“对方私钥” 应该是 “自己私钥”

image-20210719223052744

经典算法

  • RSA(可⽤于加密和签名)
  • DSA(仅⽤于签名,但速度更快)

优缺点

  • 优点:可以在不安全⽹络上传输密钥
  • 缺点:计算复杂,因此性能相⽐对称加密差很多

破解思路

  • 和对称加密不同之处在于,⾮对称加密的公钥很容易获得,因此制造原⽂-密⽂对是没有困难的事
  • 所以,⾮对称加密的关键只在于,如何找到⼀个正确的私钥,可以解密所有经过公钥加密过的密 ⽂。找到这样的私钥即为成功破解
  • 由于⾮对称加密的⾃身特性,怎样通过公钥来推断出私钥通常是⼀种思路(例如 RSA),但往往 最佳⼿段依然是穷举法,只是和对称加密破解的区别在于,对称加密破解是不断尝试⾃⼰的新密钥是否可以将⾃⼰拿到的原⽂-密⽂对进⾏加密和解密,⽽⾮对称加密时不断尝试⾃⼰的新私钥是否和公钥互相可解。

反破解

和对称加密⼀样,⾮对称加密算法优秀的标准同样在于,让破解者找不到⽐穷举法更有效的破解⼿段,并且穷举法的破解时间⾜够⻓。

Cookie

起源:「购物⻋」功能的需求,由 Netscape 浏览器开发团队打造。

作用:

  • 会话管理:登录状态、购物车等
  • 个性化:用户偏好、主题
  • Tracking:分析用户行为

⼯作机制:

  1. 服务器需要客户端保存的内容,放在 Set-Cookie headers ⾥返回,客户端会⾃动保存。
  2. 客户端保存的 Cookies,会在之后的所有请求⾥都携带进 Cookie header ⾥发回给服务 器。
  3. 客户端保存 Cookie 是按照服务器域名来分类的,例如 shop.com 发回的 Cookie 保存下来 以后,在之后向 games.com 的请求中并不会携带。
  4. 客户端保存的 Cookie 在超时后会被删除、没有设置超时时间的 Cookie (称作 Session Cookie)在浏览器关闭后就会⾃动删除;另外,服务器也可以主动删除还未过期的客户端 Cookies。

image-20210722210547371

Tracking:分析用户行为

工作机制:

  1. 客户端访问服务器
  2. 服务器返回数据的同时,附加上第三方追踪网站的一个img地址
  3. 客户端收到数据,在不知情的情况下访问第三方追踪网站的img
  4. 第三方追踪网站获取到客户端的来源(from)后,并给客户端设置cookie(client_id),从而把客户端和来源网站记录做映射记录下来
  5. 客户端再访问其它网站服务器时,服务器同样返回该第三方追踪网站的图片地址,如此重复...
  6. 当很多个form来源被第三方追踪网站收集到后, 就可以对用户进行画像了

image-20210722210754985

XSS (Cross-site scripting): 跨站脚本攻击。即使⽤ JavaScript 拿到浏览器的 Cookie 之后,发送到⾃⼰的⽹站,以这种⽅式来盗取⽤户 Cookie。 应对⽅式:Server 在发送 Cookie 时,敏感的 Cookie 加上 HttpOnly。HttpOnly——这个 Cookie 只能⽤于 HTTP 请求,不能被 JavaScript 调⽤。它可 以防⽌本地代码滥⽤ Cookie。

XSRF (Cross-site request forgery): 跨站请求伪造。即在⽤户不知情的情况下访问 已经保存了 Cookie 的⽹站,以此来越权操作⽤户账户(例如盗取⽤户资⾦)。 应对⽅式主要是 Referer 校验(浏览器帮你做Referer的设置)。

Cookie很方便,但正是由于Cookie的便捷,所以会存在很多危险性问题,因此不应存放敏感性信息。

OAuth

OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。

OAuth在"客户端"与"服务提供商"之间,设置了一个授权层(authorization layer)。 "客户端"不能直接登录"服务提供商",只能登录授权层,以此将用户与客户端区分开来。"客户端"登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。 "客户端"登录授权层以后,"服务提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。

Authorization

HTTP身份验证

Basic

  • 格式:Authorization: Basic [username:password的base64结果]

由于用户 ID 与密码是是以明文的形式在网络中进行传输的(尽管采用了 base64 编码,但是 base64 算法是可逆的),所以基本验证方案并不安全。基本验证方案应与 HTTPS / TLS 协议搭配使用。假如没有这些安全方面的增强,那么基本验证方案不应该被来用保护敏感或者极具价值的信息。

Bearer

  • 格式:Authorization: Bearer [bearer token]

  • bearer token:通过OAuth2授权流程获得

OAuth2流程

理解OAuth 2.0
OAuth 2.0 的一个简单解释
理解 OAuth 2.0 认证流程

OAuth 2.0 共有 4 种访问模式[1]

  • 授权码模式(Authorization Code),适用于一般服务器端应用
  • 简化模式(Implicit),适用于纯网页端应用,不过现在推荐使用 PKCE 作为替代
  • 密码模式(Resource owner password credentials),不介绍
  • 客户端模式(Client credentials),不介绍

另外注意 OAuth 服务本身必须是 HTTPS 的,而三方应用可以是 HTTP 的。

Authorization Code流程

  1. 第三⽅⽹站向授权⽅⽹站申请授权合作,拿到 client id 和 client secret
  2. ⽤户在使⽤第三⽅⽹站时,点击「通过 XX (如 GitHub) 授权」按钮,第三⽅⽹站将⻚⾯跳 转到授权⽅⽹站,并传⼊ client id 作为⾃⼰的身份标识
  3. 授权⽅⽹站根据 client id ,将第三⽅⽹站的信息和第三⽅⽹站需要的⽤户权限展示给⽤ 户,并询问⽤户是否同意授权
  4. 用户点击「同意授权」按钮后,授权⽅⽹站将⻚⾯跳转回第三⽅⽹站,并传⼊ Authorization code 作为⽤户认可的凭证
  5. 第三⽅⽹站将 Authorization code 发送回⾃⼰的服务器
  6. 服务器将 Authorization code 和⾃⼰的 client secret ⼀并发送给授权⽅的服务器,授权⽅服务器在验证通过后,返回 **access token**给第三方服务器。
  7. OAuth 流程结束。
  8. 在上⾯的过程结束之后,第三⽅⽹站的服务器或客户端就可以使⽤ access token 作为⽤户授权的令牌,向授权⽅⽹站发送请求来获取⽤户信息或操作⽤户账户。但这 已经在 OAuth 流程之外。

为什么 OAuth 要引⼊ Authorization code,并需要申请授权的第三⽅将 Authorization code 发 送回⾃⼰的服务器,再从服务器来获取 access token,⽽不是直接返回 access token ?这样复 杂的流程意义何在?

为了安全。 OAuth 不强制授权流程必须使⽤ HTTPS,因此需要保证当通信 路径中存在窃听者时,依然具有⾜够⾼的安全性。

oauth-authorization-code

微信认证

  1. 第三⽅ App 向腾讯申请第三⽅授权合作,拿到 client id 和 client secret
  2. ⽤户在使⽤第三⽅ App 时,点击「通过微信登录」,第三⽅ App 将使⽤微信 SDK 跳转到 微信,并传⼊⾃⼰的 client id 作为⾃⼰的身份标识
  3. 微信通过和服务器交互,拿到第三⽅ App 的信息,并限制在界⾯中,然后询问⽤户是否同 意授权该 App 使⽤微信来登录
  4. ⽤户点击「使⽤微信登录」后,微信和服务器交互将授权信息提交,然后跳转回第三⽅ App,并传⼊ Authorization code 作为⽤户认可的凭证
  5. 第三⽅ App 调⽤⾃⼰服务器的「微信登录」Api,并传⼊ Authorization code,然后等待 服务器的响应
  6. 服务器在收到登录请求后,拿收到的 Authorization code 去向微信的第三⽅授权接⼝发送 请求,将 Authorization code 和⾃⼰的 client secret ⼀起作为参数发送,微信在验证通过 后,返回 access token
  7. 服务器在收到 access token 后,⽴即拿着 access token 去向微信的⽤户信息接⼝发送请 求,微信验证通过后,返回⽤户信息
  8. 服务器在收到⽤户信息后,在⾃⼰的数据库中为⽤户创建⼀个账户,并使⽤从微信服务器 拿来的⽤户信息填⼊⾃⼰的数据库,以及将⽤户的 ID 和⽤户的微信 ID 做关联
  9. ⽤户创建完成后,服务器向客户端的请求发送响应,传送回刚创建好的⽤户信息
  10. 客户端收到服务器响应,⽤户登录成功

img