OSS可以通过阿里云STS(Security Token Service)进行临时授权访问。通过STS,您可以为第三方应用或子用户(即用户身份由您自己管理的用户)颁发一个自定义时效和权限的访问凭证。
实现逻辑:前端用户点击上传图片, 前端向后端发起请求,后端返回一个签名后的url ,前端拿到签名后url再进行用Put方法
调用OSS的SDK时报“SignatureDoesNotMatch”错误, 由于前端直接向签名url put 存在跨域问题,需要把oss的跨域给打开, 使用:set_oss_cors 方法
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : sts_token.py
# @Author: 往事随风
# @Email: gujiwork@outlook.com
# @Date : 2020/12/24
# @Desc :
# pip install aliyun-python-sdk-sts
# pip install oss2
from aliyunsdkcore import client
from aliyunsdksts.request.v20150401 import AssumeRoleRequest
import json
import oss2
import requests
from oss2.models import BucketCors, CorsRule
class AliStsGenerateToKey:
def __init__(self, endpoint, access_key_id, access_key_secret, bucket_name):
"""
:param endpoint: 地域
:param access_key_id: ram子用户key
:param access_key_secret: ram子用户 secret
:param bucket_name: oss bucket名称
"""
self.endpoint = endpoint
self.access_key_id = access_key_id
self.access_key_secret = access_key_secret
self.bucket_name = bucket_name
@staticmethod
def generate_sts_key(self, role_arn):
object_name = ''
# 设置sts token获取的权限策略, action及resource
policy_text = '{"Statement": [{"Action": ["*"],"Effect": "Allow","Resource": ["*"]}],"Version":"1"}'
# policy_text = '{"Statement": [{"Action": ["oss:GetObject"],"Effect": "Allow","Resource": ["acs:oss:*:*:oss-test/*"]}],"Version":"1"}'
clt = client.AcsClient(self.access_key_id, self.access_key_secret, 'cn-hangzhou')
req = AssumeRoleRequest.AssumeRoleRequest()
req.set_accept_format('json')
req.set_RoleArn(role_arn)
# 设置会话名称,审计服务使用此名称区分调用者
req.set_RoleSessionName('test')
req.set_Policy(policy_text)
body = clt.do_action_with_exception(req)
return body
def set_oss_sign(self, role_arn, save_oss_file_name, exp_time, upload_file):
"""
:param role_arn: 角色的资源名称
:param save_oss_file_name: 保存到oss目录/文件名 settlement_excel_download/20200812200526.png
:param exp_time: 过期时间
:param upload_file: 本地上传文件名
:return: 签名后的url地址
"""
body = AliStsGenerateToKey.generate_sts_key(self, role_arn=role_arn)
# 使用RAM账号的AccessKeyId和AccessKeySecret向STS申请临时token。
token = json.loads(oss2.to_unicode(body))
# 使用临时token中的认证信息初始化StsAuth实例。
auth = oss2.StsAuth(token['Credentials']['AccessKeyId'],
token['Credentials']['AccessKeySecret'],
token['Credentials']['SecurityToken'])
# 使用StsAuth实例初始化存储空间。
bucket = oss2.Bucket(auth, self.endpoint, self.bucket_name)
sign_url_put = bucket.sign_url('PUT', save_oss_file_name, exp_time)
print(sign_url_put)
rsp = requests.put(url=sign_url_put, data=open(upload_file, 'rb'))
if rsp.status_code == 200:
print('请求上传文件返回状态码: 200')
file_exists = bucket.object_exists(key=save_oss_file_name)
if file_exists:
sign_url = bucket.sign_url('GET', save_oss_file_name, exp_time)
print(sign_url)
if requests.get(url=sign_url).status_code != 200:
return False, '设置签名未生效或已过期, 请检查oss是否设置跨域、 oss权限及sts权限设置是否正确、过期策略时间!'
return True, sign_url
print('文件上传失败!')
return False, '文件上传失败!'
class AliOssCors:
def __init__(self, access_key_id, access_key_secret, endpoint, bucket_name):
self.access_key_id = access_key_id
self.access_key_secret = access_key_secret
self.endpoint = endpoint
self.bucket_name = bucket_name
self.auth = oss2.Auth(self.access_key_id, self.access_key_secret)
def get_oss_cors(self):
bucket = oss2.Bucket(self.auth, self.endpoint, self.bucket_name)
try:
cors = bucket.get_bucket_cors()
except oss2.exceptions.NoSuchCors:
print('OSS未设置跨域! 开始设置跨域----->')
AliOssCors.set_oss_cors(self)
AliOssCors.get_oss_cors(self)
else:
print('获取跨域规则----->')
for rule in cors.rules:
print('AllowedOrigins={0}'.format(rule.allowed_origins))
print('AllowedMethods={0}'.format(rule.allowed_methods))
print('AllowedHeaders={0}'.format(rule.allowed_headers))
print('ExposeHeaders={0}'.format(rule.expose_headers))
print('MaxAgeSeconds={0}'.format(rule.max_age_seconds))
@staticmethod
def set_oss_cors(self):
bucket = oss2.Bucket(self.auth, self.endpoint, self.bucket_name)
rule = CorsRule(allowed_origins=['*'],
allowed_methods=['GET', 'HEAD', 'PUT', 'POST'],
allowed_headers=['*'],
max_age_seconds=0)
# 注意:如果已存在的规则将会被覆盖。
bucket.put_bucket_cors(BucketCors([rule]))
def delete_oss_cors(self):
bucket = oss2.Bucket(self.auth, self.endpoint, self.bucket_name)
bucket.delete_bucket_cors()
oss_obj = AliOssCors(
access_key_id='xxxxxxxxxxxxxxxxx',
access_key_secret='xxxxxxxxxxxxxxxxx',
endpoint='oss-cn-hangzhou.aliyuncs.com',
bucket_name='xxxxxxxxxxxxxxxxx')
oss_obj.get_oss_cors()
p = AliStsGenerateToKey(
endpoint='oss-cn-hangzhou.aliyuncs.com',
access_key_id='xxxxxxxxxxxxxxxxx',
access_key_secret='xxxxxxxxxxxxxxxxx',
bucket_name='xxxxxxxxxxxxxxxxx')
p.set_oss_sign(
role_arn='acs:ram::{xxxxxxxxxxxxxxxxx}',
save_oss_file_name='test.png',
exp_time=300,
upload_file='22.png' # 本地图片
)
STS中临时授权时出现“You are not authorized to do this action. You should be authorized by RAM“报错
代码中使用的AccessKey和AccessKeySecret是主账号的,并非RAM用户的。 必须要创建子帐号才key才可以
https://help.aliyun.com/document_detail/100624.html?spm=a2c4g.11186623.2.10.5e474529lXELjN#concept-xzh-nzk-2gb
https://help.aliyun.com/document_detail/28798.html?spm=a2c4g.11186623.2.10.29bc203dUpOWmQ#reference-smb-tzy-xdb
https://help.aliyun.com/document_detail/32033.html?spm=a2c4g.11186623.2.23.64e33b49Q6fLpK#section-zx1-55k-kfc