Moon's blog

write the code, change the world.

https理论与实践

说点废话,我一直觉得APPLE是个非常激进的公司, 从早些的Flash,到现在的http,又比如新的Mac连USB口都不给。公司大到这个量级,已经可以凭一己之力促进先进技术的普及了。

本文内容分为以下三部分

  • HTTPS协议
  • 使用Let’s Encrypt在后端部署https服务
  • https在iOS上的正确使用姿势

Part1 HTTPS协议

普通的HTTP请求,在通信双方建立了TCP连接之后,就可以进行了。而HTTPS则不同,在建立TCP连接之后,需要先进行SSL协议的握手过程,然后才是HTTP的通信。

SSL的握手过程如下图所示

alice想要与bob进行https的通信,需要以下几步

  1. alice给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法。
  2. bob确认双方使用的加密方法,并给出数字证书(包含bob的公钥)、以及一个服务器生成的随机数(Server random)。
  3. alice 确认数字证书(向CA确认)有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给鲍勃。
  4. bob使用自己的私钥,获取爱丽丝发来的随机数(即Premaster secret)。
  5. alice和bob根据约定的加密方法,使用前面的三个随机数,生成”对话密钥”(session key),用来加密接下来的整个对话过程。

上述步骤的最终目的,是为了生成”对话密钥“,以后的通信都使用这个密钥进行对称加密(一般对称加解密的速度是比较快的)

那么看到这里,就又一个问题出现了。握手阶段的信息安全如何保障?

答案是无法保障。整个握手阶段,都是明文的。因此如果有第三方窃听了通信,他可以获得Client random、Server random以及加密后的Premaster secret。只要第三方无法破解Premaster secret的内容,那么通信就是安全的。

Part2 使用Let’s Encrypt在后端部署https服务

可以参考这篇文章

How To Secure Nginx with Let’s Encrypt on Ubuntu 16.04

大致步骤

  1. 安装certbot客户端
  2. 配置服务器允许方案 /.well-known文件夹
  3. sudo letsencrypt certonly -a webroot –webroot-path=/var/www/html -d example.com -d www.example.com 这个命令会生成你需要的证书等文件
  4. 配置Nginx ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

如果速度很慢多半是python的源被墙了,需要改一下pip配置。

Let’s Encrypt大法好,退沃通保平安!

Part3 在iOS上使用自己颁发的HTTPS证书的正确姿势

在开发环境,也需要进行HTTPS的话,需要对AFN进行两个设置:允许不合法的证书和不验证域名

1
2
3
4
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:[self host]]];

manager.securityPolicy.allowInvalidCertificates = YES;
manager.securityPolicy.validatesDomainName = NO;

使用vagrant统一开发环境

简介

vagrant类似现在很流行的docker ,相比起docker打包依赖的方式,vagrant打包的是整个虚拟机。

核心原理

vagrant 会把你配置好的虚拟机打包成box, 通过一个Vagrantfile配置这个虚拟机的一些行为。 其他成员只要使用你的box,就可以获得统一的开发环境。

使用

安装步骤略去不提,使用vagrant很简单

1.vagrant init 创建一个文件夹,然后cd到这个文件夹里

2.vagrant box add hashicorp/precise64 (这个命令会下载ubuntu12.04LTS,也可以从这里寻找可用的box)

3.编辑Vagrantfile

1
2
3
4
5
Vagrant.configure("2") do |config|
config.vm.box = "hashicorp/precise64"
end

注意! 这里的box值必须与第二步add的值一致!

4.vagrant up 启动虚拟机

5.vagrant ssh 登录(也可以手动ssh,注意端口是2222,例如 ssh abc@192.168.1.1 -P 2222)

6.安装你需要的各种软件,对于我是 RVM, ruby, rails , mysql, redis…

7.sudo poweroff 关闭虚拟机

8.vagrant package 把虚拟机打包成box

9.all done!!! 分发你的box吧

一个事件驱动的图片爬虫

起因

  1. 无聊的时候会翻出去看看国外的漫画,然而一页一页加载总是会很慢,偶尔还需要多刷新几次才能显示出来,非常影响体验。于是就写了个脚本去抓某一个漫画下所有的图片,这样跑一遍脚本,就能在本地看图片了。

  2. 为了偷懒,第一个版本用的单线程模型,几百张图片串行请求,真的慢。

  3. 实际工作中一直没什么机会用到异步IO,正好拿来练练手。

分析

并发的下载图片,有多线程和事件驱动两套方案。

多线程的实现方式,例如一部漫画有300张图,我不可能开300个Thread,系统受不了。比较实际的做法是使用一个容量为N的ThreadPool,那么,同时就只能发出N个请求,然后所有线程Block等待,其实效率也不高

然而事件驱动的方式就不一样了,我可以一口气把所有请求发出去,当有请求完成时,就调用事先定义的回调Handle,实现了300张图片的并行下载。

先上图看看效果

从图中就可以看出,所有的请求都发出去之后,才陆续有响应结果乱序到达。这就是典型的异步IO的情景。

基于EventMachine的异步图片爬虫

EventMachine是ruby社区知名的事件驱动库,类似于Netty、NodeJS

通过 EM.run{}就可以开始一个事件循环

以下是关键代码

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
28
29
30
31
32
33
34
35
#img_info = [{file_name: '1.jpg', url:'xxx'}...]

def getImg(img_info)
EM.run{ #开启事件循环
multi = EventMachine::MultiRequest.new #request容器
@img_info_copy = img_info.dup
img_info.each do |info|
file_name = File.join(@dir, info[:file_name])
if FileTest::exist?(file_name)
@img_info_copy.delete(info)
puts "#{file_name} skip".blue
next
end
puts "#{file_name} start".green
req = EventMachine::HttpRequest.new(info[:url]).get #创建request
multi.add "#{file_name}",req
req.callback { #成功回调
File.open(file_name, 'w') { |file| file.write(req.response) }
@img_info_copy.delete(info)
puts "#{file_name} done".green
}
req.errback { #失败回调
puts "#{file_name} fail".red
}
end
multi.callback do #所有request都完成后的回调
if @img_info_copy.size == 0 #如果没有图片下载失败
EM.stop
else #递归调用,重新下载的图片
puts "Total fails: #{@img_info_copy.size}, solving...".red
getImg @img_info_copy.dup
end
end
}
end

遇到的小坑

1
EM.run {}之后,主线程就block了,所有写在它后面的代码都不执行

效果

通过这次的优化,下载一部两三百页漫画的时间从之前单线程版本的二十多分钟,变成了现在的两分钟左右!