Почему Amazon SES принимает мои запросы get, но отклоняет мои запросы post с аналогичной структурой?

#python #amazon-web-services #post #python-requests #amazon-ses

#python #amazon-web-services #Публикация #python-запросы #amazon-ses

Вопрос:

Это код запроса get, который я в настоящее время использую для отправки электронной почты через простой почтовый сервис Amazon:

 import datetime
import hashlib
import hmac
import urllib.parse
import requests


def sign(key, msg):
    return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()


def getSignatureKey(key, dateStamp, regionName, serviceName):
    kDate = sign(('AWS4'   key).encode('utf-8'), dateStamp)
    kRegion = sign(kDate, regionName)
    kService = sign(kRegion, serviceName)
    kSigning = sign(kService, 'aws4_request')
    return kSigning


method = 'GET'
service = 'ses'
host = 'email.us-east-1.amazonaws.com'
region = 'us-east-1'
endpoint = 'https://email.us-east-1.amazonaws.com/'
access_key = 'my_access_key'
secret_key = 'my_secret_key'
my_email = 'my_email_address'

t = datetime.datetime.utcnow()
amz_date = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d')
canonical_uri = '/'
canonical_headers = 'host:'   host   'n'
signed_headers = 'host'
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp   '/'   region   '/'   service   '/'   'aws4_request'

canonical_querystring_part1 = 'Action=SendEmail' 
                              'amp;Destination.ToAddresses.member.1={}' 
                              'amp;Message.Body.Html.Charset=UTF-8' 
                              'amp;Message.Body.Html.Data={}' 
                              'amp;Message.Body.Text.Charset=UTF-8' 
                              'amp;Message.Body.Text.Data={}' 
                              'amp;Message.Subject.Charset=UTF-8' 
                              'amp;Message.Subject.Data={}' 
                              'amp;Source={}'.format(urllib.parse.quote(my_email, safe=''), 
                                                  urllib.parse.quote('<b>Html Hello.</b>', safe=''), 
                                                  urllib.parse.quote('Non Html hello.', safe=''),
                                                  urllib.parse.quote('Asyncio Subject line.', safe=''), 
                                                  urllib.parse.quote(my_email, safe=''))
canonical_querystring_part2 = 'amp;X-Amz-Algorithm=AWS4-HMAC-SHA256'
canonical_querystring_part2  = 'amp;X-Amz-Credential='   urllib.parse.quote_plus(access_key   '/'   credential_scope)
canonical_querystring_part2  = 'amp;X-Amz-Date='   amz_date
canonical_querystring_part2  = 'amp;X-Amz-Expires=30'
canonical_querystring_part2  = 'amp;X-Amz-SignedHeaders='   signed_headers
canonical_querystring = canonical_querystring_part1   canonical_querystring_part2

payload_hash = hashlib.sha256(''.encode('utf-8')).hexdigest()
canonical_request = method   'n'   canonical_uri   'n'   canonical_querystring   'n'   canonical_headers   'n'   signed_headers   'n'   payload_hash
string_to_sign = algorithm   'n'   amz_date   'n'   credential_scope   'n'   hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()

signing_key = getSignatureKey(secret_key, datestamp, region, service)
signature = hmac.new(signing_key, string_to_sign.encode('utf-8'), hashlib.sha256).hexdigest()

canonical_querystring  = 'amp;X-Amz-Signature='   signature
request_url = endpoint   "?"   canonical_querystring


r = requests.get(request_url)
  

Это немного длинновато, но выполняется нормально. Это моя попытка сделать то же самое, но с запросом post:

 import datetime
import hashlib
import hmac
import requests


def sign(key, msg):
    return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()


def getSignatureKey(key, dateStamp, regionName, serviceName):
    kDate = sign(('AWS4'   key).encode('utf-8'), dateStamp)
    kRegion = sign(kDate, regionName)
    kService = sign(kRegion, serviceName)
    kSigning = sign(kService, 'aws4_request')
    return kSigning


method = 'POST'
service = 'ses'
host = 'email.us-east-1.amazonaws.com'
region = 'us-east-1'
endpoint = 'https://email.us-east-1.amazonaws.com/'
access_key = 'my_access_key'
secret_key = 'my_secret_key'
my_email = 'my_email_address'


content_type = 'application/x-www-form-urlencoded; charset=utf-8'


# Request parameters for CreateTable--passed in a JSON block.
request_parameters = '{'
request_parameters  = "'body': {'Action': 'SendEmail', 'Destination.ToAddresses.member.1': '%s', 'Message.Body.Html.Charset': 'UTF-8', " 
                      "'Message.Body.Html.Data': 'HTMLMESSAGE', 'Message.Body.Text.Charset': 'UTF-8', 'Message.Body.Text.Data': 'nonHTMLmessage', 'Message.Subject.Charset': 'UTF-8', " 
                      "'Message.Subject.Data': 'subject','Source': '%s'}, " % (my_email, my_email)
request_parameters  = "'Content-Type': '%s', " % content_type
request_parameters  = "'context': {'client_region': 'us-east-1'}"
request_parameters  = '}'


t = datetime.datetime.utcnow()
amz_date = t.strftime('%Y%m%dT%H%M%SZ')
date_stamp = t.strftime('%Y%m%d')


canonical_uri = '/'
canonical_querystring = ''
canonical_headers = 'content-type:'   content_type   'n'   'host:'   host   'n'   'x-amz-date:'   amz_date   'n'
signed_headers = 'content-type;host;x-amz-date'

payload_hash = hashlib.sha256(request_parameters.encode('utf-8')).hexdigest()

canonical_request = method   'n'   canonical_uri   'n'   canonical_querystring   'n'   canonical_headers   'n'   signed_headers   'n'   payload_hash
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = date_stamp   '/'   region   '/'   service   '/'   'aws4_request'

string_to_sign = algorithm   'n'   amz_date   'n'   credential_scope   'n'   hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()
signing_key = getSignatureKey(secret_key, date_stamp, region, service)
signature = hmac.new(signing_key, string_to_sign.encode('utf-8'), hashlib.sha256).hexdigest()

authorization_header = algorithm   ' '   'Credential='   access_key   '/'   credential_scope   ', '   'SignedHeaders='   signed_headers   ', '   'Signature='   signature
headers = {'Content-Type': content_type, 'X-Amz-Date': amz_date, 'Authorization': authorization_header}


r = requests.post(endpoint, params=request_parameters, headers=headers)
  

Я, насколько могу, следую документации Amazon для процесса подписания v4 здесь:https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html

Я также использую полные примеры запросов get и post, которые они имеют для SES, найденные здесь:https://docs.aws.amazon.com/ses/latest/DeveloperGuide/query-interface-examples.html

По какой-то причине это не работает. Я попытался изменить kwarg в моем запросе post с params на json на data.

 r = requests.post(endpoint, params=request_parameters, headers=headers)
<IncompleteSignatureException>
  <Message>When Content-Type:application/x-www-form-urlencoded, URL cannot include query-string parameters (after '?'): '/?{'body': {'Action': 'SendEmail', 'Destination.ToAddresses.member.1': 'ArbiBushka717@gmail.com', 'Message.Body.Html.Charset': 'UTF-8', 'Message.Body.Html.Data': 'HTMLMESSAGE', 'Message.Body.Text.Charset': 'UTF-8', 'Message.Body.Text.Data': 'nonHTMLmessage', 'Message.Subject.Charset': 'UTF-8', 'Message.Subject.Data': 'subject','Source': 'ArbiBushka717@gmail.com'}, 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'context': {'client_region': 'us-east-1'}}'</Message>
</IncompleteSignatureException>

r = requests.post(endpoint, data=request_parameters, headers=headers)
<AccessDeniedException/>


r = requests.post(endpoint, json=request_parameters, headers=headers)
<InvalidSignatureException>
  <Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.</Message>
</InvalidSignatureException>
  

Каждая форма запроса post выдает различную ошибку. Кто-нибудь, имеющий опыт структурирования запросов post (или запросов к API Amazon), знает, что я делаю неправильно?

Прежде чем кто-либо напишет: «Используйте их Python SDK». Я не могу. Их SDK блокируется, и я должен делать это асинхронно. Я планирую перейти от запросов к aiohttp. Я пытаюсь получить это в запросах на данный момент, потому что так проще задать этот вопрос.

Пожалуйста, помогите, если можете, спасибо за чтение.

Комментарии:

1. Будет очень полезно, если вы сможете опубликовать фактические тела запросов

Ответ №1:

подождите nvm, я должен. Чтобы указать тело в запросах, вы используете kwarg «data».

Также моя переменная request_parameters должна была быть в формате param1=Thisamp;param2=that

Вау, какая головная боль.