欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

微服务

程序员文章站 2022-03-14 12:14:07
...

前后端分离是如何做的

在前后端分离架构中,后端只需要负责按照约定的数据格式向前端提供可调用的 API 服务即可。前后端之间通过 HTTP 请求进行交互,前端获取到数据后,进行页面的组装和渲染,最终返回给浏览器。

如何解决跨域

什么是跨域问题?
跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对 JavaScript 施加的安全限制。
什么是同源?
所谓同源是指,域名,协议,端口均相同
http://www.funtl.com --> http://admin.funtl.com 跨域
http://www.funtl.com --> http://www.funtl.com 非跨域
http://www.funtl.com --> http://www.funtl.com:8080 跨域
http://www.funtl.com --> https://www.funtl.com 跨域

使用 CORS(跨资源共享)解决跨域问题
CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制。
CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10
整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉
因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信

CORS 与 JSONP 的比较
CORS 与 JSONP 的使用目的相同,但是比 JSONP 更强大。
JSONP 只支持 GET 请求,CORS 支持所有类型的 HTTP 请求。JSONP 的优势在于支持老式浏览器,以及可以向不支持 CORS 的网站请求数据。

微服务哪些框架

Dubbo
是阿里巴巴服务化治理的核心框架,并被广泛应用于阿里巴巴集团的各成员站点。阿里巴巴近几年对开源社区的贡献不论在国内还是国外都是引人注目的,比如:JStorm 捐赠给 Apache 并加入 Apache 基金会等,为中国互联网人争足了面子,使得阿里巴巴在国人眼里已经从电商升级为一家科技公司了。

Spring Cloud
从命名我们就可以知道,它是 Spring Source 的产物,Spring 社区的强大背书可以说是 Java 企业界最有影响力的组织了,除了 Spring Source 之外,还有 Pivotal 和 Netflix 是其强大的后盾与技术输出。其中 Netflix 开源的整套微服务架构套件是 Spring Cloud 的核心。

RPC框架

什么是 RPC?
RPC 是指远程过程调用,也就是说两台服务器 A,B 一个应用部署在 A 服务器上,想要调用 B 服务器上应用提供的函数或方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。

RPC 是如何通讯的?
微服务

  1. 要解决通讯的问题,主要是通过在客户端和服务器之间建立 TCP 连接,远程过程调用的所有交换的数据都在这个连接里传输。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享同一个连接。
  2. 要解决寻址的问题,也就是说,A 服务器上的应用怎么告诉底层的 RPC 框架,如何连接到 B 服务器(如主机或 IP 地址)以及特定的端口,方法的名称是什么,这样才能完成调用。比如基于 Web 服务协议栈的 RPC,就要提供一个 endpoint URI,或者是从 UDDI 服务上查找。如果是 RMI 调用的话,还需要一个 RMI Registry 来注册服务的地址。
  3. 当 A 服务器上的应用发起远程过程调用时,方法的参数需要通过底层的网络协议如 TCP 传递到 B 服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式,也就是序列化(Serialize)或编组(marshal),通过寻址和传输将序列化的二进制发送给 B 服务器。
  4. B 服务器收到请求后,需要对参数进行反序列化(序列化的逆操作),恢复为内存中的表达方式,然后找到对应的方法(寻址的一部分)进行本地调用,然后得到返回值。
    5.返回值还要发送回服务器 A 上的应用,也要经过序列化的方式发送,服务器 A 接到后,再反序列化,恢复为内存中的表达方式,交给 A 服务器上的应用。

为什么要用 RPC?
就是无法在一个进程内,甚至一个计算机内通过本地调用的方式完成的需求,比如比如不同的系统间的通讯,甚至不同的组织间的通讯。由于计算能力需要横向扩展,需要在多台机器组成的集群上部署应用,

RPC 的实现原理
首先需要有处理网络连接通讯的模块,负责连接建立、管理和消息的传输。其次需要有编解码的模块,因为网络通讯都是传输的字节码,需要将我们使用的对象序列化和反序列化。剩下的就是客户端和服务器端的部分,服务器端暴露要开放的服务接口,客户调用服务接口的一个代理实现,这个代理实现负责收集数据、编码并传输给服务器然后等待结果返回。

说说 Dubbo 的实现原理

Dubbo 作为 RPC 框架,实现的效果就是调用远程的方法就像在本地调用一样。如何做到呢?

  1. 本地有对远程方法的描述,包括方法名、参数、返回值,在 Dubbo 中是远程和本地使用同样的接口
  2. 要有对网络通信的封装,要对调用方来说通信细节是完全不可见的,网络通信要做的就是将调用方法的属性通过一定的协议(简单来说就是消息格式)传递到服务端
  3. 服务端按照协议解析出调用的信息;执行相应的方法;在将方法的返回值通过协议传递给客户端;客户端再解析;在调用方式上又可以分为同步调用和异步调用;

你怎么理解 RESTful

2000 年,Roy Thomas Fielding 博士在他那篇著名的博士论文《Architectural Styles and the Design of Network-based Software Architectures》中提出了几种软件应用的架构风格,REST 作为其中的一种架构风格在这篇论文的第5章中进行了概括性的介绍。

REST 是“REpresentational State Transfer”的缩写,可以翻译成“表现状态转换”,但是在绝大多数场合中我们只说 REST 或者 RESTful。Fielding 在论文中将 REST 定位为“分布式超媒体应用(Distributed Hypermedia System)”的架构风格,它在文中提到一个名为“HATEOAS(Hypermedia as the engine of application state)”的概念。

我们利用一个面向最终用户的 Web 应用来对这个概念进行简单阐述:这里所谓的应用状态(Application State)表示 Web 应用的客户端的状态,简单起见可以理解为会话状态。资源在浏览器中以超媒体的形式呈现,通过点击超媒体中的链接可以获取其它相关的资源或者对当前资源进行相应的处理,获取的资源或者针对资源处理的响应同样以超媒体的形式再次呈现在浏览器上。由此可见,超媒体成为了驱动客户端会话状态的转换的引擎。

借助于超媒体这种特殊的资源呈现方式,应用状态的转换体现为浏览器中呈现资源的转换。如果将超媒体进一步抽象成一般意义上的资源呈现(Representation )方式,那么应用状态变成了可被呈现的状态(REpresentational State)。应用状态之间的转换就成了可被呈现的状态装换(REpresentational State Transfer),这就是 REST。

#总结
REST 是一种很笼统的概念,它代表一种架构风格。

说说如何设计一个良好的 API

版本号
在 RESTful API 中,API 接口应该尽量兼容之前的版本。但是,在实际业务开发场景中,可能随着业务需求的不断迭代,现有的 API 接口无法支持旧版本的适配,此时如果强制升级服务端的 API 接口将导致客户端旧有功能出现故障。实际上,Web 端是部署在服务器,因此它可以很容易为了适配服务端的新的 API 接口进行版本升级,然而像 Android 端、IOS 端、PC 端等其他客户端是运行在用户的机器上,因此当前产品很难做到适配新的服务端的 API 接口,从而出现功能故障,这种情况下,用户必须升级产品到最新的版本才能正常使用。

为了解决这个版本不兼容问题,在设计 RESTful API 的一种实用的做法是使用版本号。一般情况下,我们会在 url 中保留版本号,并同时兼容多个版本。

【GET】  /v1/users/{user_id}  // 版本 v1 的查询用户列表的 API 接口
【GET】  /v2/users/{user_id}  // 版本 v2 的查询用户列表的 API 接口

现在,我们可以不改变版本 v1 的查询用户列表的 API 接口的情况下,新增版本 v2 的查询用户列表的 API 接口以满足新的业务需求,此时,客户端的产品的新功能将请求新的服务端的 API 接口地址。虽然服务端会同时兼容多个版本,但是同时维护太多版本对于服务端而言是个不小的负担,因为服务端要维护多套代码。这种情况下,常见的做法不是维护所有的兼容版本,而是只维护最新的几个兼容版本,例如维护最新的三个兼容版本。在一段时间后,当绝大多数用户升级到较新的版本后,废弃一些使用量较少的服务端的老版本API 接口版本,并要求使用产品的非常旧的版本的用户强制升级。

注意的是,“不改变版本 v1 的查询用户列表的 API 接口”主要指的是对于客户端的调用者而言它看起来是没有改变。而实际上,如果业务变化太大,服务端的开发人员需要对旧版本的 API 接口使用适配器模式将请求适配到新的API 接口上。

资源路径
RESTful API 的设计以资源为核心,每一个 URI 代表一种资源。因此,URI 不能包含动词,只能是名词。注意的是,形容词也是可以使用的,但是尽量少用。一般来说,不论资源是单个还是多个,API 的名词要以复数进行命名。此外,命名名词的时候,要使用小写、数字及下划线来区分多个单词。这样的设计是为了与 json 对象及属性的命名方案保持一致。例如,一个查询系统标签的接口可以进行如下设计。

【GET】  /v1/tags/{tag_id} 

同时,资源的路径应该从根到子依次如下

/{resources}/{resource_id}/{sub_resources}/{sub_resource_id}/{sub_resource_property}

我们来看一个“添加用户的角色”的设计,其中“用户”是主资源,“角色”是子资源。

【POST】  /v1/users/{user_id}/roles/{role_id} // 添加用户的角色

有的时候,当一个资源变化难以使用标准的 RESTful API 来命名,可以考虑使用一些特殊的 actions 命名。

/{resources}/{resource_id}/actions/{action}

举个例子,“密码修改”这个接口的命名很难完全使用名词来构建路径,此时可以引入 action 命名。

【PUT】  /v1/users/{user_id}/password/actions/modify // 密码修改

请求方式
可以通过 GET、 POST、 PUT、 PATCH、 DELETE 等方式对服务端的资源进行操作。其中:

GET:用于查询资源
POST:用于创建资源
PUT:用于更新服务端的资源的全部信息
PATCH:用于更新服务端的资源的部分信息
DELETE:用于删除服务端的资源。
这里,使用“用户”的案例进行回顾通过 GET、 POST、 PUT、 PATCH、 DELETE 等方式对服务端的资源进行操作。

【GET】 /users # 查询用户信息列表
【GET】 /users/1001 # 查看某个用户信息
【POST】 /users # 新建用户信息
【PUT】 /users/1001 # 更新用户信息(全部字段)
【PATCH】 /users/1001 # 更新用户信息(部分字段)
【DELETE】 /users/1001 # 删除用户信息

查询参数
RESTful API 接口应该提供参数,过滤返回结果。其中,offset 指定返回记录的开始位置。一般情况下,它会结合 limit 来做分页的查询,这里 limit 指定返回记录的数量。

【GET】  /{version}/{resources}/{resource_id}?offset=0&limit=20

同时,orderby 可以用来排序,但仅支持单个字符的排序,如果存在多个字段排序,需要业务中扩展其他参数进行支持。

【GET】  /{version}/{resources}/{resource_id}?orderby={field} [asc|desc]

为了更好地选择是否支持查询总数,我们可以使用 count 字段,count 表示返回数据是否包含总条数,它的默认值为 false。

【GET】  /{version}/{resources}/{resource_id}?count=[true|false]

上面介绍的 offset、 limit、 orderby 是一些公共参数。此外,业务场景中还存在许多个性化的参数。我们来看一个例子。

【GET】  /v1/categorys/{category_id}/apps/{app_id}?enable=[1|0]&os_type={field}&device_ids={field,field,}

注意的是,不要过度设计,只返回用户需要的查询参数。此外,需要考虑是否对查询参数创建数据库索引以提高查询性能。

状态码
使用适合的状态码很重要,而不应该全部都返回状态码 200,或者随便乱使用。这里,列举在实际开发过程中常用的一些状态码,以供参考。

状态码 描述
200 请求成功
201 创建成功
400 错误的请求
401 未验证
403 被拒绝
404 无法找到
409 资源冲突
500 服务器内部错误

异常响应
当 RESTful API 接口出现非 2xx 的 HTTP 错误码响应时,采用全局的异常结构响应信息。

HTTP/1.1 400 Bad Request
Content-Type: application/json
{
    "code": "INVALID_ARGUMENT",
    "message": "{error message}",
    "cause": "{cause message}",
    "request_id": "01234567-89ab-cdef-0123-456789abcdef",
    "host_id": "{server identity}",
    "server_time": "2014-01-01T12:00:00Z"
}

请求参数
在设计服务端的 RESTful API 的时候,我们还需要对请求参数进行限制说明。例如一个支持批量查询的接口,我们要考虑最大支持查询的数量。

【GET】     /v1/users/batch?user_ids=1001,1002      // 批量查询用户信息
参数说明
- user_ids: 用户ID串,最多允许 20 个。

此外,在设计新增或修改接口时,我们还需要在文档中明确告诉调用者哪些参数是必填项,哪些是选填项,以及它们的边界值的限制。

【POST】     /v1/users                             // 创建用户信息
请求内容
{
    "username": "lusifer",                 // 必填, 用户名称, max 10
    "realname": "鲁斯菲尔",               // 必填, 用户名称, max 10
    "password": "123456",              // 必填, 用户密码, max 32
    "email": "aaa@qq.com",     // 选填, 电子邮箱, max 32
    "weixin": "Lusifer",            // 选填,微信账号, max 32
    "sex": 1                           // 必填, 用户性别[1-男 2-女 99-未知]
}

响应参数
针对不同操作,服务端向用户返回的结果应该符合以下规范。

【GET】     /{version}/{resources}/{resource_id}      // 返回单个资源对象
【GET】     /{version}/{resources}                    // 返回资源对象的列表
【POST】    /{version}/{resources}                    // 返回新生成的资源对象
【PUT】     /{version}/{resources}/{resource_id}      // 返回完整的资源对象
【PATCH】   /{version}/{resources}/{resource_id}      // 返回完整的资源对象
【DELETE】  /{version}/{resources}/{resource_id}      // 状态码 200,返回完整的资源对象。
                                                      // 状态码 204,返回一个空文档

如果是单条数据,则返回一个对象的 JSON 字符串。

HTTP/1.1 200 OK
{
    "id" : "01234567-89ab-cdef-0123-456789abcdef",
    "name" : "example",
    "created_time": 1496676420000,
    "updated_time": 1496676420000,
    ...
}

如果是列表数据,则返回一个封装的结构体。

HTTP/1.1 200 OK
{
    "count":100,
    "items":[
        {
            "id" : "01234567-89ab-cdef-0123-456789abcdef",
            "name" : "example",
            "created_time": 1496676420000,
            "updated_time": 1496676420000,
            ...
        },
        ...
    ]

一个完整的案例
最后,我们使用一个完整的案例将前面介绍的知识整合起来。这里,使用“获取用户列表”的案例。

【GET】     /v1/users?[&keyword=xxx][&enable=1][&offset=0][&limit=20] 获取用户列表
功能说明:获取用户列表
请求方式:GET
参数说明
- keyword: 模糊查找的关键字。[选填]
- enable: 启用状态[1-启用 2-禁用][选填]
- offset: 获取位置偏移,从 0 开始。[选填]
- limit: 每次获取返回的条数,缺省为 20 条,最大不超过 100[选填]
响应内容
HTTP/1.1 200 OK
{
    "count":100,
    "items":[
        {
            "id" : "01234567-89ab-cdef-0123-456789abcdef",
            "name" : "example",
            "created_time": 1496676420000,
            "updated_time": 1496676420000,
            ...
        },
        ...
    ]
}
失败响应
HTTP/1.1 403 UC/AUTH_DENIED
Content-Type: application/json
{
    "code": "INVALID_ARGUMENT",
    "message": "{error message}",
    "cause": "{cause message}",
    "request_id": "01234567-89ab-cdef-0123-456789abcdef",
    "host_id": "{server identity}",
    "server_time": "2014-01-01T12:00:00Z"
}
错误代码
- 403 UC/AUTH_DENIED    授权受限

如何理解 RESTful API 的幂等性

什么是幂等性

HTTP 幂等方法,是指无论调用多少次都不会有不同结果的 HTTP 方法。不管你调用一次,还是调用一百次,一千次,结果都是相同的。

GET     /tickets       # 获取ticket列表
GET     /tickets/12    # 查看某个具体的ticket
POST    /tickets       # 新建一个ticket
PUT     /tickets/12    # 更新ticket 12
PATCH   /tickets/12    # 更新ticket 12
DELETE  /tickets/12    # 删除ticekt 12

HTTP GET 方法,用于获取资源,不管调用多少次接口,结果都不会改变,所以是幂等的。

GET     /tickets       # 获取ticket列表
GET     /tickets/12    # 查看某个具体的ticket

只是查询数据,不会影响到资源的变化,因此我们认为它幂等。

值得注意,幂等性指的是作用于结果而非资源本身。怎么理解呢?例如,这个 HTTP GET 方法可能会每次得到不同的返回内容,但并不影响资源。

可能你会问有这种情况么?当然有咯。例如,我们有一个接口获取当前时间,我们就应该设计成

GET     /service_time # 获取服务器当前时间

它本身不会对资源本身产生影响,因此满足幂等性。

HTTP POST 方法是一个非幂等方法,因为调用多次,都将产生新的资源。

POST    /tickets       # 新建一个ticket

因为它会对资源本身产生影响,每次调用都会有新的资源产生,因此不满足幂等性。

HTTP PUT 方法是不是幂等的呢?我们来看下

PUT     /tickets/12    # 更新ticket 12

因为它直接把实体部分的数据替换到服务器的资源,我们多次调用它,只会产生一次影响,但是有相同结果的 HTTP 方法,所以满足幂等性。

HTTP PATCH 方法是非幂等的。HTTP POST 方法和 HTTP PUT 方法可能比较好理解,但是 HTTP PATCH 方法只是更新部分资源,怎么是非幂等的呢?

因为,PATCH 提供的实体则需要根据程序或其它协议的定义,解析后在服务器上执行,以此来修改服务器上的资源。换句话说,PATCH 请求是会执行某个程序的,如果重复提交,程序可能执行多次,对服务器上的资源就可能造成额外的影响,这就可以解释它为什么是非幂等的了。

可能你还不能理解这点。我们举个例子

PATCH   /tickets/12    # 更新ticket 12

此时,我们服务端对方法的处理是,当调用一次方法,更新部分字段,将这条 ticket 记录的操作记录加一,这次,每次调用的资源是不是变了呢,所以它是有可能是非幂等的操作。

HTTP DELETE 方法用于删除资源,会将资源删除。

DELETE  /tickets/12    # 删除ticekt 12

调用一次和多次对资源产生影响是相同的,所以也满足幂等性。

如何设计符合幂等性的高质量 RESTful API

HTTP GET vs HTTP POST
也许,你会想起一个面试题。HTTP 请求的 GET 与 POST 方式有什么区别? 你可能会回答到:GET 方式通过 URL 提交数据,数据在 URL 中可以看到;POST 方式,数据放置在 HTML HEADER 内提交。但是,我们现在从 RESTful 的资源角度来看待问题,HTTP GET 方法是幂等的,所以它适合作为查询操作,HTTP POST 方法是非幂等的,所以用来表示新增操作。

但是,也有例外,我们有的时候可能需要把查询方法改造成 HTTP POST 方法。比如,超长(1k)的 GET URL 使用 POST 方法来替代,因为 GET 受到 URL 长度的限制。虽然,它不符合幂等性,但是它是一种折中的方案。

HTTP POST vs HTTP PUT
对于 HTTP POST 方法和 HTTP PUT 方法,我们一般的理解是 POST 表示创建资源,PUT 表示更新资源。当然,这个是正确的理解。

但是,实际上,两个方法都用于创建资源,更为本质的差别是在幂等性。HTTP POST 方法是非幂等,所以用来表示创建资源,HTTP PUT 方法是幂等的,因此表示更新资源更加贴切。

HTTP PUT vs HTTP PATCH
此时,你看会有另外一个问题。HTTP PUT 方法和 HTTP PATCH 方法,都是用来表述更新资源,它们之间有什么区别呢?我们一般的理解是 PUT 表示更新全部资源,PATCH 表示更新部分资源。首先,这个是我们遵守的第一准则。根据上面的描述,PATCH 方法是非幂等的,因此我们在设计我们服务端的 RESTful API 的时候,也需要考虑。如果,我们想要明确的告诉调用者我们的资源是幂等的,我的设计更倾向于使用 HTTP PUT 方法。

如何保证接口的幂等性

当通过调用创建实例接口在负载均衡中创建云服务器时,如果遇到了请求超时或服务器内部错误时,客户端可能会尝试重发请求,这时客户端可以通过提供可选参数 ClientToken 避免服务器创建出比预期要多的实例,也就是通过提供 ClientToken 参数保证请求的幂等性。ClientToken 是一个由客户端生成的唯一的、大小写敏感、不超过 64 个 ASCII 字符的字符串。

如果用户使用同一个 ClientToken 值调用创建实例接口,则服务端会返回相同的请求结果,包含相同的 InstanceId。因此用户在遇到错误进行重试的时候,可以通过提供相同的 ClientToken 值,来确保负载均衡只创建一个实例,并得到这个实例的 InstanceId。

如果用户提供了一个已经使用过的 ClientToken,但其他请求参数不同,则负载均衡会返回 IdempotentParameterMismatch 的错误代码。但需要注意的是,SignatureNonce、Timestamp 和 Signature 参数在重试时是需要变化的,因为负载均衡使用 SignatureNonce 来防止重放攻击,使用 Timestamp 来标记每次请求时间,所以再次请求必须提供不同的 SignatureNonce 和 Timestamp 参数值,这同时也会导致 Signature 值的变化。

通常,客户端只需要在 500(InternetError)或 503(ServiceUnavailable)错误、或者无法得到响应结果的情况下进行重试操作。返回结果是 200 时,重试可以得到上次相同的结果,但不会对服务端状态带来任何影响。而对 4xx 的返回错误,通常重试也是不能成功的。

HTTPS 原理剖析

为什么使用 HTTPS
HTTP 协议,本身是明文传输的,没有经过任何安全处理。那么这个时候就很容易在传输过程中被中间者窃听、篡改、冒充等风险。这里提到的中间者主要指一些网络节点,是用户数据在浏览器和服务器中间传输必须要经过的节点,比如 WIFI 热点,路由器,防火墙,反向代理,缓存服务器等。

HTTP 协议,中间者可以窃听隐私,使用户的敏感数据暴露无遗;篡改网页,例如往页面插的广告内容,甚至进行流量劫持,比如有的时候你会发现域名没输错,结果却跑到了一个钓鱼网站上,因为被它劫持了。

为了解决这三大风险,HTTPS 的价值就体现出来了。

  • 内容加密,第三方无法窃听。
  • 身份认证,一旦被篡改,通信双方会立刻发现。
  • 数据完整性。防止内容冒充或者篡改。

什么是 HTTPS

HTTPS,简单的理解 HTTP 的安全版,即 HTTP 下加入 SSL 层,由两部分组成:HTTP + SSL / TLS。

HTTPS 原理剖析

微服务

  • 用户在浏览器里输入一个 https 网址,此时客户端发起 HTTPS 请求,通过 TCP 和服务器建立连接(443端口)。
  • 服务器存放 CA 证书进行处理,注意的是采用 HTTPS 协议的服务器必须要有一套数字证书,这套证书其实就是一对公钥和私钥。
  • 服务器向客户端返回证书。证书里面包含了很多信息:比如域名,申请证书的公司,公钥等。以下是一个淘宝网的 CA 证书。

微服务
微服务

  • 客户端对证书进行解析。这部分工作是有客户端的 TLS 来完成的,首先会验证公钥是否有效,比如颁发机构,过期时间等,如果发现异常,则会弹出一个警告框,提示证书存在问题。如果证书没有问题,那么就生成一个随机数,然后用证书对该随机数进行加密。
  • 向服务器发送证书加密后的随机数。
  • 服务器用它的私钥进行解密,得到了客户端传过来的随机数。
  • 服务器用客户端的随机数加密后的信息发送给客户端。
  • 客户端用之前生成的**解密服务端传过来的信息。
    以上就是整个 HTTPS 的交互过程,大家是不是对整个流程有了比较大致的了解了呢。

HTTPS 的相关场景
真实业务场景是复杂的,这里,整理 3 个项目中遇到的比较复杂的应用场景。

场景一,对 HTTPS 进行 CDN 加速
这种情况下,CA 证书需要存放在哪里呢?主要两个方案:

  • 服务器(源站)提供证书给 CDN 厂商,包括公钥证书和私钥,CDN 负责交互和内容缓存,CDN 有缓存则直接响应,以 HTTP 或 HTTPS 的形式回源。这个方案,适用仅对防劫持、防篡改有需求,而愿意提供证书给 CDN 的源站加速。
  • 服务器(源站)不提供证书,CDN 存放公钥,服务器(源站)存放私钥。在 CDN 与前端浏览器进行 TLS 的认证和秘钥协商过程中,通过安全的信道把协商过程中的信息以 HTTP 或 HTTPS 的形式转发给源网站。此方案中,CDN 不做缓存,仅以自有的加速网络,将用户的请求快速送到服务器(源站),降低公网延迟。这个方案,适用于对安全要求更高,不愿将私钥共享给 CDN 的源站加速。

场景二,对HTTPS 的域名通过 CNAME 绑定到另外一个 HTTPS 域名上
这个情况下,我们需要一个证书还是两个证书呢?

我们的方案是,两个证书。因为每个证书跟自己的域名进行绑定,即使它们都在同一个服务器上,也不能使用同一个证书。

场景三,两台服务器的证书问题
因为安全问题,CA 证书在一台服务器上,而服务部署在另外一台服务器上。这种情况就比较难办。
微服务

此时,需要借助 Nginx 进行反向代理,回源到具体的服务器。

HTTPS 设计上的借鉴
对于 HTTPS 设计上的方案,对于我们而言,有什么可以借鉴的地方么?

答案是肯定的:有。一个非常典型的方案就是 RSA 双向认证。

RSA 双向认证,顾名思义,就是用对方的公钥加密是为了保密,这个只有对方用私钥能解密。用自己的私钥加密是为了防抵赖,能用我的公钥解开,说明这是我发来的。例如,支付宝的支付接口就是非常典型的RSA双向认证的安全方案。此外,我们之前的教育资源、敏感验证码出于安全性考虑都借鉴了这个方案

相关标签: 微服务