通过 header 或者 URL 中自签名计算 signature 时,经常会遇到计算签名失败 “The request signature we calculated does not match the signature you provided” 。本文 demo 演示了如何调用 API 自签名时上传 Object 到 OSS。

使用方法

[root@izw4z home]# python PutObject.py -h
Usage: PutObject.py [options]
Options:
  -h, --help  展示帮助文档 
  -i AK       客户控制台上云账号或者 RAM 子账号的 Accesskey ,必选项
  -k SK       客户控制台上云账号或者 RAM 子账号的 AccessKeySecrety ,必选项
  -e ED       OSS endpoint 地址,OSS 控制台概述上可以看到,必选项
  -b BK       OSS bucket 名称,必选项
  -o OBJECTS  上传到 OSS 的 object 名称,必选项
  -f FI       读取本地文件的路经,必选项
python PutObject.py -i B0g3mdt -k lNCA4L0P43Ax -e oss-cn-shanghai.aliyuncs.com -b yourBucket -f localfile.txt -o aliyun.txt

请求头

PUT /yuntest HTTP/1.1
Accept-Encoding: identity
Content-Length: 147
Connection: close
User-Agent: Python-urllib/2.7
Date: Sat, 22 Sep 2018 04:36:52 GMT
Host: yourBucket.oss-cn-shanghai.aliyuncs.com
Content-Type: application/x-www-form-urlencoded
Authorization: OSS B0g3mdt:lNCA4L0P43Ax

响应头

HTTP/1.1 200 OK
Server: AliyunOSS
Date: Sat, 22 Sep 2018 04:36:52 GMT
Content-Length: 0
Connection: close
x-oss-request-id: 5BA5C6E4059A3C2F
ETag: "D0CAA153941AAA1CBDA38AF"
x-oss-hash-crc64ecma: 8478734191999037841
Content-MD5: 0MqhU5QbIp3Ujqqhy9o4rw==
x-oss-server-time: 15

注意

  • Signature 中所有加入计算的参数都要放在 header 中,保持 header 和 Signature 一致。
  • PUT 上传时,Signature 计算的 Content-Type 必须是 application/x-www-form-urlencoded 。
  • 通过 header 方式进行签名认证时无法设置过期时间。目前只有 SDK 、URL 签名支持设置过期时间。

源码

#! /us/bin/envy python
#Author: handle
#Update: 2018-09-29
from optparse import OptionParser
import urllib, urllib2
import datetime
import base64
import hmac
import sha
import os
import sys
import time
class Main():

  # Initial input parse
  def __init__(self,options):
    self.ak = options.ak
    self.sk = options.sk
    self.ed = options.ed
    self.bk = options.bk
    self.fi = options.fi
    self.oj = options.objects
    self.left = '\033[1;31;40m'
    self.right = '\033[0m'
    self.types = "application/x-www-form-urlencoded"    
    self.url = 'http://{0}.{1}/{2}'.format(self.bk,self.ed,self.oj)
  # Check client input parse
  def CheckParse(self):
    if (self.ak and self.sk and self.ed and self.bk and self.oj and self.fi) != None:
      if str(self.ak and self.sk and self.ed and self.bk and self.oj and self.fi):
        self.PutObject()
    else:
      self.ConsoleLog("error","Input parameters cannot be empty")
  # GET local GMT time
  def GetGMT(self):

    SRM = datetime.datetime.utcnow()
    GMT = SRM.strftime('%a, %d %b %Y %H:%M:%S GMT')

    return GMT
  # GET Signature
  def GetSignature(self):
    mac = hmac.new("{0}".format(self.sk),"PUT\n\n{0}\n{1}\n/{2}/{3}".format(self.types,self.GetGMT(),self.bk,self.oj), sha)
    Signature = base64.b64encode(mac.digest())

    return Signature
  # PutObject
  def PutObject(self):

    try: 
      with open(self.fi) as fd:
        files = fd.read()
    except Exception as e:
      self.ConsoleLog("error",e)

    try:
      request = urllib2.Request(self.url, files)
      request.add_header('Host','{0}.{1}'.format(self.bk,self.ed))
      request.add_header('Date','{0}'.format(self.GetGMT()))
      request.add_header('Authorization','OSS {0}:{1}'.format(self.ak,self.GetSignature()))
      request.get_method = lambda:'PUT'
      response = urllib2.urlopen(request,timeout=10)
      fd.close()
      self.ConsoleLog(response.code,response.headers)
    except Exception,e:
      self.ConsoleLog("error",e)

  # output error log
  def ConsoleLog(self,level=None,mess=None):
    if level == "error":
      sys.exit('{0}[ERROR:]{1}{2}'.format(self.left,self.right,mess))
    else:
      sys.exit('\nHTTP/1.1 {0} OK\n{1}'.format(level,mess))

if __name__ == "__main__":
  parser = OptionParser()
  parser.add_option("-i",dest="ak",help="Must fill in Accesskey")
  parser.add_option("-k",dest="sk",help="Must fill in AccessKeySecrety")
  parser.add_option("-e",dest="ed",help="Must fill in endpoint")
  parser.add_option("-b",dest="bk",help="Must fill in bucket")
  parser.add_option("-o",dest="objects",help="File name uploaded to oss")
  parser.add_option("-f",dest="fi",help="Must fill localfile path")
  (options, args) = parser.parse_args()
  handler = Main(options)
  handler.CheckParse()