分享 阿里云 OSS 全攻略

evenluo · 2016年07月27日 · 最后由 mark 回复于 2016年10月10日 · 9832 次阅读

前言

这是一篇在阿里云 OSS 使用过程中我的一些经验,希望分享给大家

准备

首先你需要一个开通了 OSS 的账户或者是阿里云主账户分配给你的有 OSS 权限的一个子账户。本文的前提条件是假设你拥有足够操作 OSS 的权限。

相关概念

RAM: RAM (Resource Access Management) 是阿里云为客户提供的用户身份管理与访问控制服务。相关资料 STS:Security Token Service,是为阿里云账号(或 RAM 用户)提供短期访问权限管理的云服务。相关资料

客户端直传模型

为了不让 app server 成为瓶颈,客户端直传模型应该是最经常使用到的模型,时序图如下图:

这样,app server 只需要向云 oss 的 sts 服务器请求临时凭据,然后再上传完毕之后接受回调,或者根据客户端提供的信息自行确认即可。 公开读的 bucket 在使用中几乎没有难度,本文不做介绍,本文介绍的是需要额外授权的文件读取

服务端实现

首先,先引入官方 gem,官方的 readme 里面已经有比较详细的用法了,建议先完整地阅读官方的 readme。

基本上一个项目可能一个 ram 账户就已经足够了,可以把生成 sts client 以单例的方式提取出来,示例代码

## sts_manager.rb

require 'aliyun/sts'
require 'qiniu'

module Oss
  class StsManager
    include Singleton

    attr_reader :sts_client

    def initialize
      @sts_client = Aliyun::STS::Client.new(access_key_id: BourneSetting.aliyun_oss_client_ak_id,
                                        access_key_secret: BourneSetting.aliyun_oss_client_ak_secret)
    end
end

在我的项目里,我把 aliyun 的 ak id 和 secret 都放到了 settinglogic 的设置中,并 ignore 掉

有了 sts client 之后,就可以依据特定的条件办法 sts token 了。阿里云用一套 policy 来表征对某个资源的操作权限,其实这是一个很复杂的列表,当然跟 aws 很像,我也不说为什么了。policy 限定这个要产生的 token 对什么资源有着什么样的操作权限,你甚至还能指定其他一些条件 (condition),但是这套规则在阿里云的文档里面比较分散,有种很混乱的感觉。大家可以重点参考这里这里 ,这里还有图形化的帮助页面

这里我们假设一个示例代码

## sts_manager.rb

    def grant_write_credential(user_id, duration=3600)
      key = "assets/user-#{user_id}/#{Time.now.to_i}.png"
      resource_description = "acs:oss:*:*:#{BourneSetting.aliyun_assets_bucket}/#{key}"

      policy = Aliyun::STS::Policy.new
      policy.allow(['oss:PutObject', 'oss:AbortMultipartUpload'], resource_description)
      session_name = "#{BourneSetting.app_name}-#{user_id}-signature-#{Time.now.to_i}"
      token = @sts_client.assume_role(BourneSetting.aliyun_arm_arn, session_name, policy, duration)
    end

在示例中,我们根据自己的业务逻辑生成了一个 key,这个你可以认为是在 bucket 上的一个路径(本质上只是模拟目录的一个做法),这里我们把上传的权限给定了这个 key,这个 key 不一定要详细到全路径,也可以使用类似于"assets/user-1/*"这样的方式表示以这个为前缀的 key 都有权上传。这个跟业务逻辑相关,但是需要注意的是:如果这个 key 上已经有文件,再次上传到这个 key 会直接覆盖这个文件,如果你的业务不允许这样的事情发生,那么最好有更为严格的 key 控制。

另外 session_name 大部分人也不知道是干什么的,这个我问过他们的支持,说这个 session_name 以后会作为审计的功能,现在暂时还没有用到,大家可以按照自己的逻辑设定。

这样我们就会从 sts 服务器获得一个sts token,里面包含 access_key_id, access_key_secret, security_token, expiration, session_name,这些字段全部都有用,这样你就可以把这个 token 和相关信息(bucket,key)返回给客户端,客户端就能开始使用了。

客户端实现

客户端其实是可以任何形式,这里我们可以用 ruby sdk 作为客户端来测试一下

user_client = Aliyun::OSS::Client.new(endpoint: BourneSetting.aliyun_oss_hangzhou_endpoint,
                                      access_key_id: "access_key_id",
                                      access_key_secret: "access_key_secret",
                                      sts_token: "sts_token")
bucket = user_client.get_bucket "bucket"
bucket.put_object "key", file: "local_path"

以上的 id,secret,sts token,bucket 和 key 都来自于服务端,如果你的 policy 没错的话,那么你现在应该已经上传了

cdn 加速

cdn 加速是一个很好的特性,便宜好用。cdn 默认支持 sts token 的鉴权方式,不需要做任何设置。

这才是本文的主题。以上的东西都很简单,能满足大部分的需求。但是对于 oss 的需求不尽相同,在特殊需求出现的时候,可能就真的没法找文档了,只能工单一遍一遍你来我往,有时候还能刨出来 sdk 的坑。

图片样式

使用 oss 的有一个好处就是能够拥有对多媒体文件比如图片和视频一定的处理能力,比如图片样式:

但是你必须使用图片服务的域名(比如 bucket-name.img-cn-hangzhou.aliyuncs.com)作为 endpoint 来访问才可以得到这个样式。 如果你启用了禁止访问原图的设置:那么通过图片服务的域名你是无法访问原图的,但是如果你还是使用 oss 的域名作为 endpoint 来访问图片,却仍然是可以访问到的。。

你可以变通的方式是,在编写 policy 的时候直接将样式写入 key 中,这样的话你生成的 sts token 就只能访问指定的样式了,比如这样的 key:

key = "key/to/test.jpg@!style"  #@!后面接样式的名称

有时候你不想把这一大堆数据给客户端让客户端自己去访问文件,想直接生成一个签名过的链接给客户端就好,那么你可以调用bucket#object_url方法直接生成一个签名链接给客户端,客户端在访问的时候云 oss 会自己做鉴权的。注意:这个方法在 0.4.1 之前版本的 sdk 是不能生成正确的签名请求的,我也是踩过坑跟作者联系过,他很快就发布了 0.4.1 的版本。目前这个版本 policy 还不支持 condition 的操作,大家需要自己拼 json。

自定义域名

现在 oss 可以有自定义域名,图片服务也可以有自定义域名,但是 cname 只能有一个,也就是说如果你想以一个自定义域名访问这两个服务域名是不可以的,还是只能通过不同的 endpoint 来达到访问的需求。

这个解决方案最好是:把图片和文件分 bucket 存储。据说阿里云 oss 也觉得这两个域名会导致开发者傻傻分不清楚,准备进行改变。

So what's the difference with Qiniu?

#1 楼 @novtopro

  1. OSS 更稳定; 过去一年中, 七牛至少有两次重大事故, 导致大范围 (全国), 长时间 (超过半天) 服务不可用
  2. OSS 更快; CDN 这块阿里实力更雄厚

#1 楼 @novtopro 功能上讲: 1,七牛的第三方文档转换服务要比阿里云多一些,但是阿里云也马上要出了。 2,因为访问控制系统,阿里云 OSS 可以对文档权限做出更多组合的精细配置,因此可以胜任更多的业务需求。

#2 楼 @42thcoder 七牛完全是人祸啊,我记得一次是域名没续费,还有次据说是光缆断了?

Nice. Thanks, guys.

需要 登录 后方可回复, 如果你还没有账号请 注册新账号