正确使用AWS S3的方式之打造自己的https图床


写过博客的人都知道图床,一个托管自己博客图片的地方,当然托管到自己的服务器另当别论。常见的图床可以是新浪博客,七牛云,imgur等,但是都是有各种问题,比如我之前使用的是七牛云(也曾在《给Github自定义域名加上HTTPS》博文上推荐使用),用起来相当不错,只可惜后来备案信息过期了,导致无法再使用自定义域名,更可悲的是,https不再支持,意味着尽管我的博客是https但由于有内容是http的,只能被浏览器认为是mixed-content的。

但是,前几天无意发现一片新大陆,使用aws s3结合cloudfront distribution 可以借助亚马逊云无缝快速托管自己的图片还自带https,而费用几乎是很小的,按量收费。

步骤概述

(如果不需要有自定义图片的域名,第三步可选)

  1. 创建一个图片s3 bucket并公开。
  2. 创建cloudfront distribution并绑定S3 bucket和默认证书以支持https
  3. 在DNS服务商(我的是cloudflare)创建图床域名,并绑定cloudfront域名地址

全球亚马逊 Or 亚马逊中国?

两者区别好像挺大的,后者曾经注册过,但是不知为什么没通过审核,可能需要公司邮箱吧。并且,如果考虑到备案等因素,建议使用全球亚马逊。(需要绑定VISA信用卡)
全球亚马逊地址是:https://console.aws.amazon.com/console/home

创建S3 Bucket(存储桶)

账号创建成功后,进入S3控制台https://s3.console.aws.amazon.com,存储桶名称以待托管域名命名,比如我的是 images.troyyang.com,其他项首先都选择默认,待会再一项一项改。
image

访问权限设置

在存储桶的权限页面,选择存储桶策略,键入下面的值:

1
2
3
4
5
6
7
8
9
10
11
12
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadForGetBucketObjects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::images.troyyang.com/*"
}
]
}

image

静态托管

存储桶创建成功后,进入属性页面,选择静态网站托管,键入索引文件index.html,错误文档error.html,然后保存。此时,公共访问页面已经生成,终端节点如下:
http://images.troyyang.com.s3-website-ap-northeast-1.amazonaws.com

image
上传自己的所有图片在此存储桶下,然后加上文件后缀就应该可以访问了,然而现实是残酷的,在我大天朝下,这个地址有时候是无法访问当的 。。。WTF。。。于是,得进行下面的自定义域名步骤

CloudFront Distribution

上面地址是AWS自动生成的访问域名,并且只支持http,想要支持https,并且绑定自定义域名(images.troyyang.com),需要使用到CloudFront Distribution。

CloudFront Distribution 是AWS的内容分发(CDN)使得全球各地都能以最快的速度访问到AWS最近的节点(对于中国,最近的是东京,经测,也已经足够快),并且可绑定或者生产SSL证书。

创建 Distribution

打开 https://console.aws.amazon.com/cloudfront/home, 选择Create Distribution, 传输方式选择Web选项 Get Started,在很多选项中,主要注意几项就好了(都是可后期修改):

  • Origin Domain Name中选择刚才所建的S3 Bucket 域名
  • Alternate Domain Names(CNAMEs)填写自定义域名(没有的话,可不管), 这里是 images.troyyang.com
  • SSL Certificate 暂时选默认Default CloudFront Certificate (*.cloudfront.net)
  • Price Class 可以只选择Use Only US, Canada, Europe and Asia

image

一切配置好后,静静等待几个小时就会看到Distribution部署成功,大致结果如下:

image

此时,得到Distribution 的新访问地址 d2dxo9yo9kwqp2.cloudfront.net,这个时候,我们找一张在S3中存在的图片,加上https再次访问 https://d2dxo9yo9kwqp2.cloudfront.net/2017-5-21-https.png 一切OK

自定义证书(可选)

上面的证书是亚马逊自己提供,如果想要使用绑定自己的域名证书,可以使用AWS的Certificate Manager 证书服务,在自己的DNS服务商比如万网或者阿里云那里配置好验证方式,具体操作方法参考 https://docs.aws.amazon.com/zh_cn/acm/latest/userguide/gs-acm-request.html 。因为我暂时觉得没必要,所以没使用上。

绑定自定义域名(可选)

上面的是cloudfront分发的一个地址,虽然地址是固定的,但毕竟不是自家的域名,感觉不高大上,所以需要绑定上自己的图片域名。

由于我的DNS服务解析改为了Cloudflare,所以是以Cloudflare的来配置的域名,但和万网或者阿里云的配置完全一致,在DNS解析项中添加一条CNAME记录,指向Cloudfront分配的域名即可

image
等待绑定解析成功后,访问 https://images.troyyang.com/2017-5-21-https.png ,一切OK

Top

Stripe开发使用指南--国际支付(含支付宝)


前段时间,因为Jason让我帮忙把Stripe支付集成到他个人网站上去,让我有机会接触到支付系统开发,同时也因为苦于没有找到太多中文方面相关文档介绍,所以做个总结,也方便以后有需要的同学。

关于Stripe支付

第一次听说Stripe还是在几个月前的一个新闻上了解到,大致说的是美国总统都在使用它,极有可能成功下一个Paypal。这么受欢迎的一个支付平台到底有什么好处呢?我粗略搜集了一下:

  • 一条代码让你网站支持繁琐的国际支付功能。(对于创业公司,再合适不过)
  • 向全球化业务拓展会成为Stripe的机会。即使支付货币不同、方法不同,Stripe都能打通各自的渠道,让全球化交易不受支付阻碍。
  • 市值超过90亿美元,和Tweeter,Lyft,Best Buy等以及国内的 Alipay, WeChat等有合作

重点说下第二点,什么意思呢,就是说客户可以使用人民币支付,如果商家(收款方)是美国的银行的话,就自动转成美元,是英国的银行就自动转为英镑!(可惜暂时不支持商家是中国(但Stripe也可提供解决方案,就是使用Atlas去创建一个美国的代理公司)

而对于我们程序员的话,当然最关心第一条,因为他的宗旨就是开发极简,对开发人员超级友好!至于多友好呢,请往下看。

最简洁支付

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Stripe Checkout</title>
</head>
<body>
<form action="/your-server-side-code" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="[Publishable key]"
data-amount="999"
data-name="troy yang"
data-description="Widget"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-locale="auto"
data-zip-code="true"
data-currency="eur">
</script>
</form>
</body>
</html>

就这么几行代码,我们就已经实现了客户端所有事:

image

真的是超级简单,但是这种方式是基于信用卡支付的界面,已经可以满足一半的支付方式,对于其他的三方支付,比如3D secure, 支付宝,微信,甚至比特币,Stripe为我们提供了其他方式,等下我就使用支付宝来举例。

注册 Stripe 账号

和注册支付宝账号一个道理,首先注册账号,然后绑定自己银行卡,BUT, 就像前面提到的,不支持中国,所以就算注册成功,也没法激活,也就没法收款。

对于中国商家怎么办呢,我能想到的就只有这几个办法:

  • 自己去支持国家去办理张银行卡
  • 使用国外的朋友银行卡
  • 使用Atlas

对于Jason来说,因为他是英国人,所以他可以创建他的主账号,然后添加我的stripe账号到他team memeber账号列表中,这样我就可以访问他账户下所有开发者需要的权限。邀请成功后,Dashboard页面

两个阶段

Stripe有两种模式,一个是测试模式(Test Mode),一个是生产模式(Live Mode),测试模式下产生的金钱交易都只用于测试,当所有测试通过后即可切换为Live模式。唯一的不同就是Publishable keySecret key, 一会我们会用到这两个值。
image

交易流程

Stripe有几个概念用于整个交易阶段和状态:
image

创建 Source

使用自己的Publishable key来创建一种source(比如Cards, 3D Secure, 支付宝,甚至比特币等), 创建source完了后,就会得到一个用于交易的Token或者是一个跳转到其他支持的三方支付平台(比如支付宝支付)页面等待用户支付。当用户支付(或者取消支付)完成,自动跳转回到指定结果页面。用户支付页面结束后,可能会得到三个状态:

  • source.chargeable 用户授权(支付)成功
  • source.failed 用户拒绝授权(支付)
  • source.canceled 超时支付

创建 Charge

当用户支付成功后,此时在Stripe端的支付状态变为source.chargeable,意思就是授权成功了,你可以在我支付宝平台上扣钱啦,所以,此时我们还需要使用Secret key来创建Charge来完成,官方推荐的是使用webhooks来捕捉状态,并且完成Charge的创建。当Charge完成后,整个支付完成,会得到一个charge.succeeded的状态。

使用 webhooks

Webhooks 里提供了几十种状态,所有这些状态都会注册到Stripe里一个叫webhooks事件钩子的地方,我们可以指定不同事件的触发时,转发数据到某个我们自己搭建好的Web Api上。(下图是我们的服务器end point, 因为我们没有用到服务器,使用的是亚马逊lambda做一个Serverless)
image

举个支付宝的栗子

服务端 (Serverless)

以AWS的Lambda + API gateway为例, 其中,前者是用来定义API, 后者是做路由。
image

image

image

创建Charge代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
'use strict';

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
exports.handler = (event, context, callback) => {
console.log("request: " + JSON.stringify(event));

let stripeData = event.data.object;
stripe.charges.create({
amount: stripeData.amount,
source: stripeData.id,
currency: stripeData.currency || 'usd',
description: 'My Englishtutor 30 days' || ('Stripe payment ' + event.id),
}, function(err, charge) {
if (err && err.type === 'card_error') {
context.fail(new Error(err.message));
}
else if (err) {
context.fail(err);
}
else {
context.succeed({ status: charge.status, success: true });
}
});
};

客户端 (Web)

多种实现方式:

Checkout

文章开头那段

的集成代码就是使用的checkout方式,非常简单。集成代码直接帮你完成了客户端的部分,服务端只需要定义好source.chargeable的钩子API 就好了。

在做支付宝开发的时候,发现可以直接使用Checkout的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<form action="https://xxx.execute-api.eu-central-1.amazonaws.com/stripepayment/xxx" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="pk_test_xxx"
data-amount="30000"
data-name="myenglishtutor.eu"
data-label="Pay With Alipay"
data-description="30 days"
data-image="/images/logo.png"
data-locale="auto"
data-alipay="auto"
data-currency="usd">
</script>
</form>

但是总是得到这个错误:

1
Unrecognized request URL (POST: /v1/alipay/send_sms). Please see https://stripe.com/docs or we can help at https://support.stripe.com/.

image

发邮件给Stripe support team得到的结果是为了以后的扩展,Stripe不再提供alipay的checkout方式, 无奈,只得使用下面的方式。

Stripe.js & Elements

当然对于如果你觉得Checkout的方式集成度太高,不够灵活,那Stripe.js是你最好的选择。

Stripe.js其实就是客户端的一个JS核心类库,Elements是它的UI类库,其实上面的Checkout代码就是Stripe使用两者给我们封装好了的,避免我们直接接触敏感信息,但是其实质都是一样的,都是用来创建source。这里就直接贴出客户端的代码了(这里没有用到Elements做UI,因为就是一个按钮支付,太简单,所以没用到):

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
var stripe = Stripe('pk_live_xxxx');

function alipay(amount) {
showLoading();
stripe.createSource({
type: 'alipay',
amount: parseInt(amount),
currency: 'gbp', // usd, eur,
redirect: {
return_url: 'https://xxx.eu/pay/result.html'
},
}).then(function (response) {
hideLoading();
if (response.error) {
alert(response.error.message);
}
else {
processStripeResponse(response.source);
}
});
}

function processStripeResponse(source) {
window.location.href = source.redirect.url;
}

image

这里需要注意几点:

  • currency 必须是Stripe账号所在地货币,也就是绑定的银行卡所在地,因为Jason是英国人,所以必须使用gbp(这里愚蠢如我的犯了一个常识错误,一直以为英国也是欧盟的,所以使用eur,结果怎么也不对,直接哭晕在厕所)
  • return_url指向的是当用户重定向到我们常见的支付宝支付页面后,跳转回支付完成的页面,在这个返回页面中,因为支付宝是同步完成支付的,所以我们可以去查询charge.succeeded的状态来判定是否用户支付是否完成。

当一切OK,点击支付按钮,就会跳转到支付宝支付页面(其他支持的三方平台也可以),如下:
image

Top

成都持续交付大会

抽空记录一下2017年11月18日中国第十三届持续交付大会的内容,总的来说收货满满,扫盲了很多持续集成,持续交付以及众多分布式相关的技术。

image

关键点

微服务架构

微服务毫无疑问是这两年最火的架构模式之一,可惜没有机会实践,但是了解到的是重点在于如何划分服务,更多的可以参考cnblogs上的一篇博文,很全。
image

京东架构演变

听完演讲者的京东架构演变的过程充分验证了金字塔不是一天建成的,互联网产品一直是迭代的,两张京东架构图足以说明他们的技术演变过程:

架构1.0:
image

架构3.0:
image

Serverless

感觉Serverless 会是另一个未来软件开发的趋势,自己的博客系统里就集成了很多serverless的服务,对于我来说最常见的可能就是类似各种评论系统像DISQUS,多说, (哎,可惜已经关了),存储系统等。当然对企业来说的话,像亚马逊的Lambda这种更能方便客户快速拼接出各种产品,减少开发人员成本。

这个月也参加了亚马逊用户社区活动,里面更是重点提到了Serverless的广泛运用,听完更是为Severless 打call。

扫盲篇(未完):

elasticsearch

jmq

binlog

数据库分库分表(取模)

jproxy

服务找寻(取模)

TRUNKED BASED DEVELOPMENT

A/B 测试

蓝绿部署

分布式调用链追踪原理:在相互每个服务请求header中添加trackId

契约测试框架:janus, pact, pacto, spring cloud contract

开发环境区分:dev-qa-sys-perf-uat-prod (我们公司使用的有点不一样localdev-stage-dev-qa-uat-prod)

Top
Top

windows 下搭建https + node.js + nginx

最近做一个微信小程序的时候因为要求所有请求都得是https的连接,服务器端https 倒是搭建好了,可本地测试没法进行啊,于是只能自己在本地搭建个https的服务。步骤很少,和把大象放进冰箱需要的步骤一样!只需要三步:第一步:要使用ssl,肯定需要生成证书,这里我就生成的自签名证书。 第二部:安装nginx和配置ssl。第三部:用nginx反向代理到node服务端口

Read More

Top
Top
Top

Hexo-lazyload-image图片懒加载

最近在看Google Chrome新出的一个API是无意间想到了对图片的懒加载,后来想想自己的网站还不支持呢,索性花了些时间让网站给支持上了,并发现Hexo上还没有一个懒加载的插件,又倒腾着写了个hexo的插件hexo-lazyload-image,并发布到NPM上供大家使用,从这几天下载数来看看来大家还是很有这个需求 :)

Read More

Top

Javascript中原型链继承的简单理解

以前对于Javascipt中的继承,大部分只是基于代码层面,理论理解虽然看了很多,总是当时理解了过几天确又忘了怎么的了。这两天又看了一遍《Javascrip面向对象编程指南》,其中在说到原型链的时候有一段话解释了很多以前自己容易混淆的地方:

首先我们知道每个对象都会有一个构造器,而原型本身也是一个对象,这意味着它必然也有一个构造器,而这个构造器又会有自己的原型,于是这种结构就会持续下去,形成一个原型链。

实践出真知

理解这段话并不难,可如果没有在实践中去理解,就会像以前一样,老是记不住。

实例对象的各种属性

先看看最简单的内置对象string类型的各种构造函数(构造器)和原型,a一定是实例化的对象,而不是构造函数(构造函数一般是大写)。

image

从上面我们可以很直观的看到很多东西:

  • a是由String构造函数(constructor)创建的。
  • a的构造器函数(constructor)是有原型(prototype)的。
  • a是没有原型属性(prototype)的。
  • 神秘的proto直接是对a构造器的原型的引用。

所以如果a里有我们继承的原型属性值rating,我们平时就可以这样使用:

1
2
a.constructor === String
a.constructor.prototype.rating === a.rating

构造函数的原型

在JS中实现继承的方式有很多,而我最喜欢这种Mozilla推荐的:

1
2
3
4
5
function User(auth) {
BaseManager.call(this, auth);
}
User.prototype = Object.create(BaseManager.prototype); // IE8 不支持Object.create
User.prototype.constructor = User;

代码其实不难,也容易理解,首先是构造函数内调用基类,然后是原型覆盖,最后是构造函数重新赋值。其中最最最应该理解的是User是个构造函数而不是实例化的对象,只有构造函数是有prototype属性的,这和上面的实例化对象a是不一样的。

Top

Travis-ci自动编译部署github上的项目

在使用Hexo写完一篇博客后,都需要手动在本地编译,并生成静态文件,最后在上传至github服务器上才能发布,繁琐步骤姑且不说,万一哪天换了台电脑,没有Hexo环境的时候如何写博客呢?要是直接在github源码里写好文章后能自动编译发布就好了,好在github的好基友travis可以轻松帮我们同时实现这种CI,CD。

Read More

Top