1. 地球号对象服务
首先,对象服务包含JSON对象和FILE对象。这是两套接口,再加上obj通用这套接口,一共一套接口。但创建对象的即包含addFobj,addJobj
fobj: 文件对象,它的创建和删除,修改,都是函数计算触发的。所以不占接口列表。 jobj: json对象,它的创建,删除,修改,都占接口列表。
这里面有个非常重要的讨论就是: jobj的创建和计数都是可控的,控制在每个app,shop,token_user_id,token_own_id,token_id这个级别。
但fobj就不行了,包含:
- 拥有OSS权限的人可以直接用OSS工具上传。
- 如果A用户知道了B用户的channel,也可以把文件传入进去,触发B用户文件记录和计费。可以恶意攻击B用户的商业行为。
我们的做法,在OSS上创建足够多的RAM用户,赋予不同的用户不同的权限。
[对象存储 OSS > 产品简介 > 功能概览]https://help.aliyun.com/document_detail/52830.html?spm=5176.8465980.entries.6.3c991450fq9jb7
我们尤其注意OSS权限的做法
ACL:OSS 为权限控制提供访问控制列表(ACL)。ACL是授予存储空间和对象访问权限的访问策略。 您可以在创建存储空间或上传对象时配置ACL,也可以在创建存储空间或上传对象后的任意时间内修改ACL。
[!note|label:ACL能做什么?] ACL的处理粒度,要么是对整个Bucket,要么是对某个具体的文件。 Bucket Policy:您可以通过控制台的Bucket Policy功能方便直观地授权其他用户访问您的OSS资源,例如向其他账号的RAM用户授予访问权限,以及向匿名用户授予带特定IP条件限制的访问权限。 [!note|label:Bucket方式能做什么?] ACL的处理粒度,要么是对整个Bucket,要么是对某个具体的文件。 [!note|label:RAM能做什么?] RAM Policy:您可以构建RAM Policy来控制存储空间和文件夹的访问权限。OSS提供了RAM策略编辑器帮助您快速生成RAM Policy。RAM Policy Editor
既然阿里云建议用BucketPolicy,我们就用BucektPolicy
针对存放在 Bucket 的 Object 的访问,OSS 提供了多种权限控制方式,包括 ACL、Bucket Policy 和 RAM Policy。 ACL:OSS 为权限控制提供访问控制列表(ACL)。ACL是基于资源的授权策略,可授予 Bucket 和 Object 访问权限。 您可以在创建 Bucket 或上传 Object 时设置 ACL,也可以在创建 Bucket 或上传Object 后的任意时间内修改 ACL。 RAM Policy:RAM (Resource Access Management)是阿里云提供的资源访问控制服务。RAM Policy 是基于用户的授权策略。通过设置 RAM Policy,您可以集中管理您的用户(比如员工、系统或应用程序),以及控制用户可以访问您名下哪些资源的权限。比如能够限制您的用户只拥有对某一个 Bucket 的读权限。子账号是从属于主账号的,并且这些账号下不能拥有实际的任何资源,所有资源都属于主账号。RAM Policy 操作比较复杂,强烈推荐您使用 Bucket Policy。 Bucket Policy:Bucket Policy 是基于资源的授权策略。相比于 RAM Policy,Bucket Policy 操作简单,支持在控制台直接进行图形化配置,并且 Bucket 拥有者直接可以进行访问授权,无需具备RAM 操作权限。Bucket Policy 支持向其他账号的 RAM 用户授予访问权限,以及向匿名用户授予带特定IP条件限制的访问权限。
2. 所以要参考的文件是
选择子账号:您可以从下拉菜单中选择当前账号的子账号,授予资源访问权限。若账号较多时,您也可以直接在搜索框输入子账号名称,搜索支持模糊匹配。您的账号必须是主账号,或拥有此Bucket管理权限及RAM控制台ListUsers权限的子账号,否则无法查看当前账号的子账号列表。 其他账号:您可以给其他主账号、子账号以及通过STS生成的临时用户授予访问权限。 当您需要给其他主账号或子账号授权时,请输入被授权账号的UID。 当您需要给临时用户授权时,输入格式为arn:sts::{RoleOwnerUid}:assumed-role/{RoleName}/{RoleSessionName}。例如生成临时用户时使用的角色为testrole,角色拥有者的主账号UID为12345,生成临时用户时指定的RoleSessionName为testsession。此时应填写arn:sts::12345:assumed-role/testrole/testsession。生成临时授权用户的操作请参见STS临时授权访问OSS。 匿名账号():若您需要给所有用户授权,可以选择匿名账号()。
[!note|label:注意]
- 当被授权的用户是其他阿里云主账号时,该账号无法通过OSS控制台访问授权资源,您可以通过命令行工具ossutil、图形化工具ossbrowser、OSS SDK、OSS API访问授权资源。
- 当被授权的用户是STS临时用户时,该账号无法通过OSS控制台访问授权资源,您可以通过命令行工具ossutil、OSS SDK、OSS API访问授权资源。 给RAM用户授予ListUsers权限请参见为RAM用户授权。
注意,一个子用户可以赋予多个channel的权限,而我们要做的是把一个子账户和多个channel在数据库关联起来。
我们要做的是,
- 管理员用户可以通过ossutil和windows的ossclient上传文件。整个好说,是我们自己内部人做的。
- 开发人员可以通过临时申请STS临时授权。这个不好弄。 如何让不同的用户
我们以前的做法是在让STS服务具备osser的角色(这个角色可以访问所有OSS资源)函数计算直接从context上下文获取角色的子账户的accesskeyid和accessKeySecret,这样很危险,是把子账户的返回了。
# -*-# -*- coding: utf-8 -*-
def handler(event, context):
response = {
'isBase64Encoded': False,
'statusCode': 200,
'headers': None,
'body': {
"access_key_id":context.credentials.access_key_id ,
"access_key_secret": context.credentials.access_key_secret,
"security_token" : context.credentials.security_token
}
}
p
return response
我们的做法,规定bucket的子文件夹为最小控制粒度。但avatar.xdua.com是个特例。我们很希望有avatar.xdua.com/12345678.jpg 这样的短码路径。这样对user表压力也小。另外avatar仅仅是头像,它有自己独立的应用特征
1:文件都很小 2:只接受putObject接口,不支持断点续传,和multipart上传。 3:正因为只有putObject接口,我们就可以轻松的从obj的etag上获取它的MD5,然后转成8位短码。 4:头像上传还需要返回头像地址,因为网页上要立刻刷新显示。 5: 头像存储的OSS对象名不能有路径,这个必须SDK来控制。
avatar与普通的obj还不一样,
3. 如何搞定头像上传
【本方案已废弃】
第一步:创建一个专门上传头像的子账户,因为头像服务是全局服务,整个地球号就只需要一个。同时也创建全控账户,全空账户主要用来在控制台调用delObj来删除不用的obj的
第二步:

这个账号是可读/可写
,那为什么不选择完全控制
呢,因为完全控制,意味着可以删除,但我们不需要删除。
第三步:php集成OTS
https://github.com/ShaoZeMing/Aliyun-Sts
第四步:浏览器端上传obj的时候必须有对应的meta,否则传入就失败。

第五步:在后台读取上传的文件里相关的meta从oss.obj读出来存入数据库。

4. 地球号对象的skema
地球号的skema包含如下
skema | 解释 |
---|---|
int | 一个整形数据 |
string | 一个字符串 |
fobj | 一个文件对象{"etag": "FC3FF98E8C6A0D3087D515C0473F8677", "owner": "35006007"} okey就是文件的路径,加上前缀 http://file.xdua.com/ 就促成了文件对象的全路径 |
5. 如何解决OSS死循环?
在阿里云控制台删除某个文件对象,引发BUcket里的真实文件被删除,删除的结果是,引发dua_trg_fobj函数计算。这并不是一个死循环,而是一个多余迭代。 解决方法:dua_trg_foby不要对文件删除敏感触发,删除操作只能在地球号控制台进行。
在地球号控制台硬删bucket里的某个文件,导致dua_trg_fobj被触发。 解决方法:dua_trg_foby不要对文件删除敏感触发,删除操作只能在地球号控制台进行。
怎么免疫文件删除,在dua_trg_fobj的对象触发器那里,不要加DeleteObj的相关触发。
6. 为什么要在文件上传的时候设计meta-objr-id标签?
obj的每个objcato都是用户自己创建的,但我们也有公用的cato。功能要支持如下功能
公共的objc,大家都可以访问。尤其是神店创建的那么多官方objc,要有一个方法可以管理这个可以这个访问,哪个可以访问。
有些开发者创建的Objc,这个是zone的属性。开发者可以配置,自己的Objc可以让哪个zone的user_id通过哪个app来访问。这种控制其实是控制了osssts的发放。
现在就是objr这个标签命名太难听了。
6.1. 问题,如果程序员通过别的channel申请获取一个ststoken,然后上传的时候通过token检查。上传到channl2里,完美绕过上传验证,如何应对?
一个方法是,可以把得到的授权做成token,但开发者仍旧可以用这个机制来绕过去。从公共的频道获取token,用到本次上传。
7. 如何设计文件上传机制?
第一步:确定objc_id(这个可以在公告文档中,也可以在开发者提供的目录)
地球号提供几个公共的cato,这个是地球号提供的免费公共服务。比方说
`TempCato`这个cato,你可以上传文件到这个cato,但文件保留24小时候就会被自动删除。
第二步:通过sts接口,获取一个临时文件上传sts.key/secret,并且有一个token用于放到meta里
这个地方要向接口提供 objc_id,而发起者自己的authorization会告诉sts接口自己的shop,app_id,user_id. sts接口还会返回一个objacs_id,这个objacs_id是个临时创建的表项。是个8位短码。 obja表存了对象名,channel,要传的文件名。
第三步:客户端上传文件时把obja_id放到meta里,
第四步: OSS触发函数dua_trg_fobj会检查obja_id,
通过obja_id搜查obja表,获取objc_id,okey,app 比较obja_表预定的okey和oss_event里的objk是否一致?
如果object_key一致,就可以把这个对象写入表了。
如果检查失败,就会标记例外到obje表里
如果检查成功,就会记录到obj表
i
8. 文件fobj_jkey命名规范,必须是一个8位的cato号和后续的oejbect_key。
现在的问题是,如果强硬把objc_id植入到objk的命名里。会出现这样的问题
+ 开发者A创建了一个`ExampleC`的objc。
+ 开发者A把它广告给其它所有人。
+ 用户开始把文件传到这个目录
+ 文件越来越多
+ 开发者A注销了`ExampleC`这个objc。
+ 这个目录下的文件都失效了。
+ 开发者A删除了`ExampleC`这个objc,也相当于删除了这个目录。
+ 这个目录下的所有文件都消失了。
+ 这些文件不可以重用了
这个问题很严重,我们需要再三考虑。
考虑的结果是:objc_id一旦创建,就不能改变。obj_key中一定要有个8位的objc_id。这样创建的文件obj一旦创建,就不能改变了。
9. 最终的修改
我们发现之前的设计都是不正常的,要考虑的方面太多,还有meta的事情,感觉已经失控了。所以,我们的做法是,继续寻求简单的做法
在STS上做文章。做法是:当你要上传文件的时候,你需要这个目录的授权。你获取STS的时候,就要指明白我要这个目录。
参考: https://help.aliyun.com/document_detail/32122.html 向 STS 申请临时 token 时,还可以指定自定义的 STS Policy。这样申请的临时权限是所扮演角色的权限与Policy指定的权限的交集。下面的例子将通过指定 STS Policy 申请对my-bucket的只读权限,并指定临时 token 的过期时间为 15 分钟。
require 'aliyun/sts'
require 'aliyun/oss'
sts = Aliyun::STS::Client.new(
access_key_id: '<子账号的AccessKeyId>',
access_key_secret: '<子账号的AccessKeySecret>')
policy = Aliyun::STS::Policy.new
policy.allow(['oss:Get*'], ['acs:oss:*:*:my-bucket/*'])
token = sts.assume_role('<role arc>', '<session name>', policy, 15 * 60)
client = Aliyun::OSS::Client.new(
endpoint: 'ENDPOINT',
access_key_id: token.access_key_id,
access_key_secret: token.access_key_secret,
sts_token: token.security_token)
bucket = client.get_bucket('my-bucket')