needrunning009

needrunning009 查看完整档案

北京编辑  |  填写毕业院校  |  填写所在公司/组织 www.cnblogs.com/needrunning/ 编辑
编辑

分享,记录工作学习过程中的收获,心得,体会。主要涉及分布式系统设计,MoongoDB,消息队列,PHP技术栈,ASP.NET技术栈,项目管理和团队敏捷开发等相关的内容。同时在维护公众号《图南日晟》,欢迎关注,一起交流学习。

个人动态

needrunning009 关注了专栏 · 6月22日

程序猿成长计划

关注 932

needrunning009 回答了问题 · 4月10日

go语言开启go module

~/.bash_profile 中增加

export GO111MODULE=on

source ~/.bash_profile 还是提示 GO111MODULE没有开启。

奇怪这个 GO111MODULE本身和GO 程序是如何关联的?

关注 6 回答 5

needrunning009 收藏了问题 · 3月7日

当查询的数据来自多个数据源,有哪些好的分页策略?

在业务系统开发中,有个列表页展示的数据比较多,数据需要从多个数据源查询,然后再将数据汇总,整理展示。

数据源可能有两种情况:

  1. 有可能数据来源不同的库。
  2. 有可能数据来源不同的API。

查询数据整合后,如何分页呢?

目前想到的方案:

  1. 数据同步,将所需数据源汇总到一张表中,然后相当于单表分页,如果数据源是 API 获取的,那怎么办呢?
  2. 内存中分页,将所数据查询出来,存放到内存中进行逻辑分页。
  3. 将查询出来的数据,汇总写入到 mongo 中,通过 mongo 进行分页。

感觉处理起这种多数据源,列表分页操作很麻烦,你有哪些很好的办法?

needrunning009 收藏了问题 · 3月7日

当查询的数据来自多个数据源,有哪些好的分页策略?

在业务系统开发中,有个列表页展示的数据比较多,数据需要从多个数据源查询,然后再将数据汇总,整理展示。

数据源可能有两种情况:

  1. 有可能数据来源不同的库。
  2. 有可能数据来源不同的API。

查询数据整合后,如何分页呢?

目前想到的方案:

  1. 数据同步,将所需数据源汇总到一张表中,然后相当于单表分页,如果数据源是 API 获取的,那怎么办呢?
  2. 内存中分页,将所数据查询出来,存放到内存中进行逻辑分页。
  3. 将查询出来的数据,汇总写入到 mongo 中,通过 mongo 进行分页。

感觉处理起这种多数据源,列表分页操作很麻烦,你有哪些很好的办法?

needrunning009 回答了问题 · 3月7日

当查询的数据来自多个数据源,有哪些好的分页策略?

我遇到过同样的问题。 一个项目的查询中心,数据源分布在关系型和MongoDB中。

对于业务来说,用户需要一个完整的业务场景,而数据会被分散到 MongoDB 和 Mysql 或者(SqlServer 中),也就是 Sql 和 Nosql 共存。

分页和权限都涉及到数据整合和中转,随即而来的是查询性能压力和维护复杂性。

这种问题的原因一般是系统初期对业务理解不充分,设计不合理,后期功能增加造成的业务债。 要从设计和功能拆分的角度彻底解决问题。

当然了,如果想维持现状,有几点建议
1 索引优化,代码优化
2 在业务可接受的情况下,做异步处理,这种仅仅适合导出类功能,可以异步导出。

还有一点,不管数据源有几种,做分页数据源也只能有一份主数据源,倾向于后端做分页为佳。

一则小故事-和时间一起做MongoDB的朋友

之前我整理的MongoDB使用中,有这个案例描述数据源分散.png

关注 5 回答 3

needrunning009 发布了文章 · 2月15日

系统服务化构建-客户端与服务器端数据一致性探讨

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。

本文从Apollo框架的客户端实现原理展开讨论。

客户端实现原理.png

原文链接
Apollo 配置中心:分布式部署

上图简要描述了 Apollo 客户端的实现原理:

1.客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送
2.客户端会定时从 Apollo 配置中心服务端拉取应用的最新配置(防止推送机制失效导致配置不更新)
3.客户端从 Apollo 配置中心服务端获取到应用的最新配置后,会保存在内存中
客户端会把从服务端获取到的配置在本地文件系统缓存一份,在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
4.应用程序从 Apollo 客户端获取最新的配置、订阅配置更新通知。

推拉结合及本地文件辅助

从户端实现原理可以看出,配置中心即控制中心,作为配置中心客户端和服务器端一致性是强需求。为了实现这个目的,采用了推拉结合及本地文件辅助的方式。

这里边涉及的关键词有 服务器,客户端,通知,长连接,同步,广播。

之前在项目中多次遇到到这种客户端需要接收到实时推送的需求,需求的本质就是要把服务器端数据及时更新到客户端,是一个数据发现和同步的过程。

本文中把这种需求定义为推送服务需求,从实现的便利性来说,都是采用第三方推送服务,实际上是推服务,然后把推送成功率全部压在这个服务的成功率上,众所周知的原因,客户端推送到达率存在一定的变数,关联因素比较多。最终这个需求的业务方满意度会大打折扣。

从客户端实现原理图中可以得到一些启示,如果想提高这个推送功能的可用性,需要从连接方式入手,采用推拉结合的方式。推方式依赖第三方服务,拉服务借助客户端自助定时结合策略,原理类似于消息补偿。

参考链接

Apollo 配置中心:分布式部署

阿波罗分布式配置中心


文章已同步到公众号《图南日晟》欢迎关注
图南日晟.png

查看原文

赞 0 收藏 0 评论 0

needrunning009 发布了文章 · 2月15日

PHP研习社-数据导出工具-php-ext-excel-export扩展使用札记

业务背景

PHP环境下的导出功能优化改造,单次同步导出数据量在2万之上。 原有导出功能使用的PHP5.6+PHPExcel.这一次换成了PHP7.2+php-ext-excel-export。

官方github地址 https://github.com/viest/php-ext-excel-export

扩展安装

安装扩展失败的追踪记录

官方安装扩展的建议

Liunx 下使用

pecl install xls-writer

开发环境

PHP 7.2.13 (cli) (built: Dec 8 2018 12:27:01) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.2.7, Copyright (c) 1999-2018, by Zend Technologies

CentOS 6.7

编译安装扩展报错

sudo make && make install

In file included from /usr/local/src/php-ext-excel-export/library/third_party/minizip/zip.c:186:
/usr/local/src/php-ext-excel-export/library/third_party/minizip/crypt.h:35: error: expected ‘;’, ‘,’ or ‘)’ before ‘*’ token
/usr/local/src/php-ext-excel-export/library/third_party/minizip/crypt.h:48: error: expected ‘;’, ‘,’ or ‘)’ before ‘*’ token
/usr/local/src/php-ext-excel-export/library/third_party/minizip/crypt.h:65: error: expected ‘;’, ‘,’ or ‘)’ before ‘*’ token
/usr/local/src/php-ext-excel-export/library/third_party/minizip/crypt.h:94: error: expected ‘;’, ‘,’ or ‘)’ before ‘*’ token
/usr/local/src/php-ext-excel-export/library/third_party/minizip/zip.c: In function ‘zipOpenNewFileInZip4_64’:
/usr/local/src/php-ext-excel-export/library/third_party/minizip/zip.c:1245: error: ‘curfile64_info’ has no member named ‘crypt_header_size’
/usr/local/src/php-ext-excel-export/library/third_party/minizip/zip.c:1251: error: ‘curfile64_info’ has no member named ‘pcrc_32_tab’
/usr/local/src/php-ext-excel-export/library/third_party/minizip/zip.c:1254: error: ‘curfile64_info’ has no member named ‘pcrc_32_tab’
/usr/local/src/php-ext-excel-export/library/third_party/minizip/zip.c:1255: error: ‘curfile64_info’ has no member named ‘crypt_header_size’
/usr/local/src/php-ext-excel-export/library/third_party/minizip/zip.c: In function ‘zip64FlushWriteBuffer’:
/usr/local/src/php-ext-excel-export/library/third_party/minizip/zip.c:1375: error: ‘curfile64_info’ has no member named ‘pcrc_32_tab’
/usr/local/src/php-ext-excel-export/library/third_party/minizip/zip.c:1375: error: ‘curfile64_info’ has no member named ‘pcrc_32_tab’
/usr/local/src/php-ext-excel-export/library/third_party/minizip/zip.c: In function ‘zipCloseFileInZipRaw64’:
/usr/local/src/php-ext-excel-export/library/third_party/minizip/zip.c:1610: error: ‘curfile64_info’ has no member named ‘crypt_header_size’
make: *** [library/third_party/minizip/zip.lo] Error 1

在github 以Issues(https://github.com/viest/php-ext-excel-export/issues/139
)的方式与官方沟通后,大家得出两个结论

1 可以尝试使用 yum install php-pecl-xlswriter 解决

yum install php-pecl-xlswriter 

2 编译报错是因为服务器centos版本过低造成

贡献者回复

This problem only occurs in CentOS6, because CentOS6 is too old.

技术关键点

对于数据导出功能的开发,这里主要说同步导出,对于数据同步导出功能有几个关键路径。

1 不管使用哪种导出扩展或者组件,数据都有一个重新组装的过程,应该尽量减少这个过程中的数组循环次数和数据库或者第三方服务的交互次数。多次循环的性能浪费会触碰到PHP的性能底线,如运行超时。

2 对于业务方来说,多数希望一次导出一个周期内的数据,比如一个月或者3个月。如果数据的来源是直接访问数据库而来,那么有一点非常重要,这里的数据库访问SQL一定要采用分页的形式,每页的pagesize可以比较大,比如3000,或者5000。切忌采用一次按照查询条件查询出来所有数据,这样在生产环境中肯定是一个隐患,运行一段时间后,总会遇到瓶颈。

php-ext-excel-export这个扩展的性能还是可以的,验证过同步导出5-6万的数据还可以应付。

当然了,对于web页面导出这样的功能,如果我是产品经理,我更倾向与使用异步方式,以报表箱的形式呈现结果。你要问我原因,留个悬念,我会单独写个总结文章。

核心代码

扩展安装

sudo yum install php-pecl-xlswriter

Total 968 kB/s | 147 kB 00:00
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
Installing : libxlsxwriter-0.8.7-1.el6.remi.x86_64 1/2
Installing : php-pecl-xlswriter-1.2.5-1.el6.remi.7.2.x86_64 2/2
Verifying : php-pecl-xlswriter-1.2.5-1.el6.remi.7.2.x86_64 1/2
Verifying : libxlsxwriter-0.8.7-1.el6.remi.x86_64 2/2

Installed:
php-pecl-xlswriter.x86_64 0:1.2.5-1.el6.remi.7.2

Dependency Installed:
libxlsxwriter.x86_64 0:0.8.7-1.el6.remi

扩展使用

渲染数据,设置对齐方式,设置头部粗体

  /**
   * 渲染数据
   *
   * @param mixed $header excel标题栏
   * @param mixed $data     数组
   *
   * @return mixed
   */
  public function make($header, $data)
  {
      /* 适用于新扩展1.2.5 start*/
      $fileHandle = $this->handle->getHandle();
      $format = new \Vtiful\Kernel\Format($fileHandle);
      $alignStyle = $format
          ->align(Format::FORMAT_ALIGN_LEFT, Format::FORMAT_ALIGN_LEFT)
          ->toResource();
      $this->handle->header($header)
          ->data($data)
          ->setRow('A1', 30, $boldStyle)->setRow('A1', 30, $alignStyle)
          ->output();
  }

  /**
   * 导出
   *
   * @return mixed
   */
  public function output()
  {
      $res = $this->handle->output();
      if ($res) {
          return ['root' => $this->filePath, 'file' => $this->fileName];
      }
  }

常用基本命令

查看扩展版本

php --ri xlswriter

show
xlswriter support => enabled
Version => 1.2.5
libxlsxwriter headers version => 0.8.7
libxlsxwriter library version => 0.8.7

pear与 pecl区别

pear is php package management.
pecl is php extension management.

查看扩展模块

php -m |grep xlswriter

相关沟通链接

https://github.com/viest/php-ext-excel-export/issues/134#issuecomment-507160228

https://github.com/viest/php-ext-excel-export/issues/139


文章已同步到公众号《图南日晟》欢迎关注
php-ext-excel-export扩展使用札记-图南日晟.jpg

查看原文

赞 1 收藏 0 评论 0

needrunning009 发布了文章 · 2月15日

PHP研习社-VS Code 扩展巡礼-REST Client

基本介绍

REST Client 是 VS Code 商店的一个 HTTP 访问扩展,官方地址 REST Client,用于模拟 HTTP请求。

我们可以在官方介绍中关注两点

1 基本的 HTTP 请求模拟,HTTP 输入和 HTTP 响应
2 VS Code 自带的辅助功能,如自动补全 Http Content-Type 等信息

VS Code 扩展巡礼-REST Client-图南科技.png

REST Client 的优势

REST Client 是一个 Http 请求模拟插件,有人会说我用 PostMan 也可以实现同样的功能,没错。但是在我看来,REST Client 的优势以下几点

1 与 VS Code 结合,方便开发人员进行接口单元测试。

2 基于手写 HTTP 报文进行请求,报文有严格的格式,编写者可以对 HTTP 协议和报文进行深入理解,如何设置请求头,如何组织不同 Content-Type 的请求参数,都会涉及到。

3 在前后端分离已经成为开发团队主流标配的情况下,开发人员可以对后端接口进行单元测试,QA 人员可以进行单独验收,对于这些日常操作,REST Client 是一款高效可靠的工具。

HTTP 报文内容格式

以下是 VS Code REST Client 中常用的两种请求方式的 HTTP 报文内容

application/json 请求

GET

GET https://example.com/topics/1 HTTP/1.1

POST

POST https://example.com/comments HTTP/1.1
content-type: application/json

{
"name": "sample",
"time": "Wed, 21 Oct 2015 18:27:50 GMT"
}

application/x-www-form-urlencoded 请求

GET

GET https://example.com/comments
?page=2
&pageSize=10

POST

POST https://api.example.com/login HTTP/1.1
Content-Type: application/x-www-form-urlencoded

name=foo
&password=bar

如下图中所示,可以看到请求端请求和服务端返回,蓝色框中是跨域相关的设置

REST Client-图南科技.png

发送嵌套 json 请求

Content-Type: application/json

{
    "type":1,
    "data":{
        "vcc_code" : "",
        "server_num" : "0102000558",
        "cus_phone" : "100",
        "cus_phone_areacode" : "",
        "cus_phone_areaname" : "",
        "cus_phone_type" : "TEL",
        "ag_name" : "20",
        "ag_num" : "30",
        "ag_phone" : "8001",
        "endResult_code" : "12",
        "user_data" : []
    }
}

HTTP Language

Add language support for HTTP request, with features like syntax highlight, auto completion, code lens and comment support, when writing HTTP request in Visual Studio Code. By default, the language association will be automatically activated in two cases:

  1. File with extension .http or .rest
  2. First line of file follows standard request line in RFC 2616, with Method SP Request-URI SP HTTP-Version format

以上是 github 文档中的说明,大致两个意思

1 Http 报文的文件后缀可以是.http 或者.rest. 这样 VS Code 可以识别出来这是报文文件,自动提示,色彩显示呀,很漂亮。

项目中可以将报文以 http 文件的形式归档整理,作为项目的一部分。

2 报文的格式,严格按照 RFC 2616,主要是一行一个元素。

再说 ContentType

这里做一个简单的基础内容回顾

multipart/form-data:当需要在表单中进行文件上传时,使用该格式
application/json:application/json 这个 Content-Type 作为响应头大家肯定不陌生。

实际上,现在越来越多的人把 application/json 作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串

application/x-www-form-urlencoded:默认的 contencType,当 form 表单请求为 get 时,数据被编码为 key/value 格式(name1=value1&name2=value2…),然后把这个字串 append 到 url 后面,用 ? 分割,加载这个新的 url 发送到服务器(表单默认的提交数据的格式)。

当请求为 post 时,浏览器把 form 数据封装到 http body 中,然后发送到 server。(form 的 enctype 属性为编码方式,常用有两种:application/x-www-form-urlencoded 和 multipart/form-data,默认为 application/x-www-form-urlencoded。

常见问题梳理

1 ContentType 不对应

简单来说 ContentType 的作用是来约定请求方与接收方对于 HTTP 报文的处理位置,请求方关心传递参数,服务方关心接收参数。如果 ContentType 不一致,就是造成传递与接收参数的位置不对应,客户端传值正确,服务器端接收不到值的情况。

现在大家都提倡使用 application/json,少数项目或者开发者还是坚持使用 Content-Type: application/x-www-form-urlencoded

2 报文格式不准确

切记一行一组元素

看看头部的格式

Post http://dev.com/api/v1/c/history/list HTTP/1.1
Content-Type: application/json
app-key: 562986529
time-stamp: 1553581026034
token: 9f3f7cd61ca7a4d24954c1bfc3e6ead7

token 虽然是字符串,但是没有引号。key 冒号空格
报文的书写有助于深入理解 Http 报文原理。

开发案例 case

我们以 PHP 语言为例, 编程中请求接口主要使用的组件有 Curl,Zend,Guzzle 这三种。

Curl 请求

使用 Curl 请求,需要注意的依然是 x-www-form-urlencoded 下的参数传递问题

php curl 转为 x-www-form-urlencoded 方式的坑

网上转变的方法基本都是写添加下面这句:

curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));

但加上去后却根本没效果。

要想以 x-www-form-urlencoded 方式发送,最关键其实是发送的数据格式。

Content-type from-data 时发送的数据用的是 array 格式,而 Content-type 为 x-www-form-urlencoded 时需要用 key=value&key=value 的格式发送,发送的是 string 型的数据。

from-data 数据的为:
​​​​​​$data = [
'name' => 'xiaoming',
'sex' => 1
];

x-www-form-urlencoded 时的数据则要变为

$params_string = http_build_query($params);

否则

$params_json = json_encode($params);

guzzle 组件中的 content-type

组件官方文档,请移步这里 Guzzle, PHP HTTP 客户端

x-www-form-urlencoded 请求

如何发送一个 application/x-www-form-urlencoded POST 请求.

guzzle-form.png

json 请求

guzzle-json.png

Guzzle 访问实例初始化失败

我们看看另一种异常

如果我们的地址拼写错误导致远程接口不能解析,会出现什么情况?

 /**
     * Post请求
     *
     * @return void
     */
    protected function post()
    {
        try {
            $this->request = $this->client->post($this->uri, ['form_params' => $this->params], $this->option);
        } catch (\GuzzleHttp\Exception\RequestException $e) {

            var_dump($error);
            die();
            Yii::warning($e);
        }
    }
2019-07-13 09:39:06 [127.0.0.1][-][-][warning][application] GuzzleHttp\Exception\ConnectException: cURL error 6: Could not resolve: xx.cn (Timeout while contacting DNS servers) (see http://curl.haxx.se/libcurl/c/libcurl-errors.html) in /Users/name/sources/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php:185
Stack trace:

总结

本篇文章从 VS Code 下的扩展 REST Client 入手,通过对基本概念,优势和应用场景的介绍,结合 HTTP 请求中关于 Content-Type 的使用注意事项,对常见的接口调用问题进行了梳理。顺便引出访问接口通常使用的 Curl,Zend,Guzzle 三种组件。

REST Client 更多的使用场景和功能扩展,可以参考官方文档,发现更多好玩有用的功能。RestClient

文章已同步到公众号《图南日晟》欢迎关注
VsCode 扩展巡礼-REST Client-图南日晟.jpg

查看原文

赞 0 收藏 0 评论 0

needrunning009 发布了文章 · 2月15日

系统服务化构建-两方OAuth

鉴于微博,QQ,微信等开发平台的影响,互联网界的工程师都知道OAuth协议,对Token 存储用户信息的机制有所了解,却很少有人提及两方OAuth这个概念。

两方OAuth

说到OAuth协议,潜意识里都会想到涉及认证,客户,和服务器,进而是认证服务器,客户端应用和资源服务器三方。

现在通用的OAuth协议是OAuth2.0,是一个互联网开放授权协议,用于规范资源服务器,客户端应用,授权服务器三者的职责,实现客户端应用在不直接获取到普通用户的用户名和密码的前提下,访问用户的私有资源。

在实际应用开发过程中,我们的应用复杂性没有达到一定规模时,应用程序只涉及到客户端APP和服务器端中心云服务的认证和业务处理。我们可以对OAuth2.0协议进行简化,演变为两方OAuth。

Auth-history.png

上图[Auth-history.png]是几个服务器客户端验证的阶段。

OAuth 协议更多的可以理解为是一个宽泛的协议,在工程应用方面只关注规范,不要求细节。
最初的OAuth协议是服务器根据HTTP 协议头的Authorization 字段验证客户端身份。而如今逐渐演变成使用Token 的方式标记客户端身份,存储用户状态信息,至于Token如何生成,在HTTP 协议中如何传输,并没有过多硬性要求。

消费者APP

在OAuth协议体系中,消费者是指开发者开发的APP,这里的APP 更是一个广义的概念,不局限在安卓和iOS应用这两种类型。PC网站,移动端WebView也被认为是APP

服务端,各种云平台如何识别这些应用,借助于应用编号和应用密钥机制,应用编号就是APP_ID,或者APP_KEY,用于唯一识别应用。密钥就是APP_SERCRET,用于请求签名等安全算法的入参,和服务器端验证的凭据。

APP_KEY和APP_SERCRET的分配和管理是实现两方OAuth的第一步

读到这里,或许你有疑问了,上文说到的不同APP,无非是安卓,iOS,WebView,我们何不定义不同的枚举来标明不同的客户端,甚至可以使用User-Agent判断。

1 PC,2 安卓 3 iOS 4 微信

这样的分类可以解决吗,答案是很难。

实际上这是两个概念,从操作系统的角度划分更多的是一种数据来源渠道的概念,而APP_KEY的本质属性是接入云端平台的开发者应用标记。

Token 生命周期

以下是一个简版的Token生命周期模型

分配

Token由客户端发起,服务器云端负责分配,典型的场景,在用户登录成功后分配,一定有效期内过期,支持每次请求不一致,token滑动过期机制。默认30分钟失效。

生成规则

用户标记key: userid+应用编号。md5(userid+盐钥+时间戳) 盐值固定。生成规则可以根据项目个性化调整,Token值不可逆。

存储

服务器端 key value形式存储到redis中,key为token,value为加密值
客户端 Token,按需存储在本地,后续接口调用时使用

失效

过期时间内失效,如初始分配60分钟,那60分钟后自动生效。
退出时,需要调用接口,删除Token。

这里会引出一个思考

退出功能需要网络支持吗?

这个问题的缘由是我发现有些工程师,退出功能是这么做的,页面跳转,清除本地Token。

Token代表的是用户状态,这种状态代表的是客户端与服务端的一种关联关系,退出功能是切断这种关联。

HTTP 是无状态的,单纯的做请求响应,而业务必须是有状态的,否则业务无法流转和推进,这种状态交给Token负责,二者是如何关联的。是Token设计中需要考量的。

一个相对完整的Token落地机制是实现两方OAuth的第二步

签名算法

签名(Sign)用于保证数据的真实性和完整性,从时效,合法性,频率几个维度处理。

对所有待签名参数按照字段名的ASCII码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value​1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符,最后加&secret=app_secret; 对string1作签名算法,字段名和字段值都采用原始值不进行URL转义。具体签名算法为sign = md5(string)。
除登录外所有接口,由服务端决定开启或者关闭签名验证。业界叫验签。

签名就用到了APP_SECRET

sign-token.png

设计要点

采用前后端分离,将接口参数分为系统级别参数和业务级别参数。
系统级参数主要包括app_key,timestamp,token,os_type,sign,主要置于HTTP 请求头位置。
业务参数基于业务需求,采用POST 或者GET方法按需传递。

检测请求超时

通过客户端时间戳timestamp,来确定真正的客户端发起时间。
服务器端检测时间差,客户端请求时间与服务器时间的差值与超时时间做对比。例如,我们可以约定时间差大于5分钟间隔的请求为无效请求,或者超时请求。

识别终端

使用App-Key的方式识别应用终端,企业内部不同的应用,由云端服务统一管理。

场景1

进行个性化配置 ,来自于微信公众号的登录用户的过期时长设置为一天,而来自于PC官网的用户的登录过期时长设置为一周。
场景2

公司某个产品线的普通版和定制版的区分,使用App-Key,对功能的简易复杂度,收费模式做梯度设置。

场景3

应用于SASS应用识别,多租户数据隔离,在数据库对应用数据做隔离。进而为相关的运营数据分析提供支持。

其它设计规约

1 后台配置关闭启用开关,开发阶段可以关闭验证,建议开启。
2 启用验证后,前端需要有公共方法计算请求签名。后台需要增加安全验证模块验证请求合法性,负责登录过期失效检测,参数完整性和权限验证。
3 安全方面关注安全弹性,安全验证级别通过timestamp, sign, token参数,三个维度配合,逐层升级。通过增加验证条件和复杂性增强安全级别。安全级别做到合理即可,没有一概而论。

总结

以上给出了一套基于两方OAuth方式的云端,客户端服务设计模型。在应用服务不复杂,业务场景允许的前提下,使用两个OAuth方式,伴随着业务发展,可以逐步演化为基于三方身份的OAuth协议工程实现。
Token机制和签名机制也可以 独立分层,与业务应用分离,演化为网关系统。
这样的设计符合架构领域的简单,演化原则,是一种很实惠的应用架构方案。


图南日晟.jpg

查看原文

赞 2 收藏 2 评论 0

needrunning009 发布了文章 · 2月15日

系统服务化构建-跨域CROS

文本讨论关于接口开发中的跨域 CORS。

CORS是一种浏览器协议,源于HTTP 请求的安全策略,在这个体系中的关键词有,同源策略,XMLHttpRequest,Ajax,和前后端分离。尤其是在目前业界前后端分离的大趋势下,跨域是一种常见的前后端开发通讯(communicate)方式。

The basic idea behind CORS is to use custom HTTP headers to allow both the browser and the server to know enough about each other to determine if the request or response should succeed or fail.

以上一段话参考 Cross-domain Ajax with Cross-Origin Resource Sharing/,主要含义是说CORS的核心思想是通过HTTP请求头通讯,使得客户端和服务器端彼此决定请求和响应是否被成功接受。

Using CORS这里有一篇关于跨域技术的完整阐述,感兴趣的可以阅读。

CORS 协议的实现需要客户端和服务器端配合协作完成。也就是我们通常所说的跨域设置。

The browser adds some additional headers, and sometimes makes additional requests, during a CORS request on behalf of the client

简单请求

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

Microsoft API设计指导 中这么一段关于跨域的描述

8.1.1. Avoiding preflight

Because the CORS protocol can trigger preflight requests that add additional round trips to the server, performance-critical apps might be interested in avoiding them. The spirit behind CORS is to avoid preflight for any simple cross-domain requests that old non-CORS-capable browsers were able to make. All other requests require preflight.

A request is "simple" and avoids preflight if its method is GET, HEAD or POST, and if it doesn't contain any request headers besides Accept, Accept-Language and Content-Language. For POST requests, the Content-Type header is also allowed, but only if its value is "application/x-www-form-urlencoded," "multipart/form-data" or "text/plain." For any other headers or values, a preflight request will happen.

The spirit behind CORS is to avoid preflight for any simple cross-domain requests that old non-CORS-capable browsers were able to make

我们提炼出简单请求的判断标准

1 )GET, HEAD or POST 三种请求
2) 不增加任何额外的请求头
3) POST 请求允许三种Content—Type: "application/x-www-form-urlencoded," "multipart/form-data" or "text/plain.

非简单请求

不符合简单请求的,均属于非简单请求。这么看来我们平时经常使用的

Content-Type: application/json

显然是一种非简单请求头。

对于非简单请求,CORS 机制会自动触发浏览器首先进行 preflight(一个 OPTIONS 请求), 该请求成功后才会发送真正的请求。这里的 preflight 也可以被翻译为(预检)请求。

CROS 流程.png

Access-Control-Allow-Headers

像上文中所说那样, 增加了自定义字段后,跨域请求就变成了一种带有 preflight 的非简单请求,因此会有下面的一种理解。

Access-Control-Allow-Headers是 preflight 请求中用来标识真正请求将会包含哪些头部字段,也就是下文中的自定义头部。这种方式是服务器端安全防范的一种。

Access-Control-Allow-Credentials

这个设置是关于是否支持Cookies的

xhr.withCredentials = true;

Access-Control-Allow-Credentials: true

YII2 中的跨域设置

//Access-Control-Allow-Origin:*
    public $arr_acao = [
        '*'
    ];
    //Access-Control-Allow-Methods
    public $arr_acam = [
        'POST', 
        'PUT', 
        'GET', 
        'DELETE',
        'OPTIONS'
    ];
    //Access-Control-Allow-Headers
    public $arr_acah = [
        'token', 
        'app-key',
        'content-type',
    ];

 header('Access-Control-Allow-Origin: '.implode(',', $this->arr_acao));
        header('Access-Control-Allow-Methods: '.implode(',', $this->arr_acam));
        header('Access-Control-Allow-Headers: '.implode(',', $this->arr_acah));
        header("Access-Control-Max-Age: 86400");

        if ($req->isOptions) {
            $code = "202";
            $message = "Accepted";
            header("HTTP/1.1 ".$code." ".$message); 
            exit();
        }

通过 Access-Control-Allow-Headers 设置自定义字段的方式,是一种安全策略,服务端要求请求头必须携带 'token', 'app-key','content-type'三个属性字段,缺一不可,否则请求不能达成。

origin 'http://xx.cn' has been blocked by CORS policy:
Request header field timestamp is not allowed by Access-Control-Allow-Headers in preflight response.

Nginx中的跨域

location ~* \.(eot|ttf|woff|woff2|svg)$ {
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers X-Requested-With;
    add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
}

以上设置的前提是Nginix 开启了模块 ngx_http_headers_module

Nginx中的其它CORS配置

#
# 用于nginx的开放式CORS配置
#
location / {
     if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        #
        # 自定义标题和标题各种浏览器*应该*可以,但不是
        #
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        #
        # 有效期为
        #
        add_header 'Access-Control-Max-Age' 22000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
     }
     if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }
     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }

Nginx中跨域,应用程序与web服务器存在耦合,增加了应用程序部署和扩展的复杂性,按需使用。

CROS 总结

本文主要介绍了CROS的基本分类和常见的实现方案,对于同源策略,XMLHttpRequest请求等基础知识被没有过多涉及。简单请求和非简单请求的分类是重点。理解了这一点,就能理解什么场景浏览器会发起预检请求,并回复对应的响应。

我们常说跨域设置是客户端和服务器端一起配合的结果,官方协议更倾向于让开发者对于跨域无感知,而浏览器与后端服务的交互和相互信任是核心。

参考资料

I want to add CORS support to my server
Using CORS


图南日晟.jpg

查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 40 次点赞
  • 获得 61 枚徽章 获得 4 枚金徽章, 获得 24 枚银徽章, 获得 33 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2013-06-27
个人主页被 2.3k 人浏览