前一些天,遇到了一个需要使用直传的场景,现在我来把这个过程记录下来。
访问S3
在访问s3时,我使用的是两套环境,
本地
本地测试时用aws-cli创建的credential文件,本地上传时的鉴权,
可以自动读取。前端本地访问时,也可以直接使用这个方案,在环境配置完成,引入对应平台的SDK后,
在操作时,SDK都会自动完成鉴权操作,无需用户参与。
线上
在服务器上配置好相关角色,线上的前后端代码会自动读取配置,完成鉴权操作。例如在Python Flask中,使用
boto3时,无需关注如何初始化凭证信息,在创建时,直接使用即可:
1 | s3_client = boto3.client('s3', region_name=region_name) |
图片上传
服务端上传
如果通过服务端上传,不难实现,参考官方文档中给出的示例代码即可:
1 | try: |
注释:file_name 其实就是文件存储的路径,bucket 是存储桶的名称,
object_name 指的是object_key,ACL指定了访问策略,这里是公开访问
前端直传
现在我们需要实现前端的直传,于是开始考虑实现两套方案
使用STS临时授权
想法是,使用sts模块的功能,依据当前环境中的角色权限生成sts的对象,此时sts对象的权限和本地就几乎一致,
后端凭此生成凭据后,返回给前端使用。
此时前端需要接入aws的sdk,使用对应的操作接口,即可完成上传,注意此时的凭证有效期是由后端指定的。
问题
在该方案执行过程中,发现有比较严重的安全隐患,返回给前端的凭据可以生成一个相当于有完整本地
权限的role,虽有时效性限制,但依旧不可行,故放弃了该方案。
方案示例
1 | sts_client = boto3.client('sts', region_name=region_name) |
使用presigned URL
presigned URL可以提供一个预签名的URL,规定了上传的对象类型和请求类型,非常好的匹配了我们当前的需求。
我们依旧是通过boto3来获取:
1 | s3_client = boto3.client('s3', region_name=region_name) |
这段代码中,规定了执行的操作,上传的目的位置(存储桶),上传的文件的key和访问策略,该presigned URL的有效时长, 以及该URL可以使用的请求方法。
前端实现
1 | let config = { |
其中,presignedUrl是后端返回的内容,file就是需要上传的file对象,需要注意的是请求header中无需额外指定content-type,不然会提示403错误, 即presignedURL的签名不匹配的问题。
这样前端直传应该就成功了。
对了,成功了是没有返回值的似乎。