AWS Presigned URL 이란?
A user who does not have AWS credentials or permission to access an S3 object can be granted temporary access by using a presigned URL.
요즘 진행하는 프로젝트에서 AWS S3 를 활용해 이미지 데이터를 처리할 일이 생겼다.
AWS S3에서 presigned url 생성 중 겪은 에러 처리 방법을 공유한다.
boto3 documentation에 친절하게 설명과 생성 함수까지 작성해준다.
import logging
import boto3
from botocore.exceptions import ClientError
def create_presigned_url(bucket_name, object_name, expiration=3600):
"""Generate a presigned URL to share an S3 object
:param bucket_name: string
:param object_name: string
:param expiration: Time in seconds for the presigned URL to remain valid
:return: Presigned URL as string. If error, returns None.
"""
# Generate a presigned URL for the S3 object
s3_client = boto3.client('s3')
try:
response = s3_client.generate_presigned_url('get_object',
Params={'Bucket': bucket_name,
'Key': object_name},
ExpiresIn=expiration)
except ClientError as e:
logging.error(e)
return None
# The response contains the presigned URL
return response
바로 적용해본다.
내가 작성한 코드 스니펫
def init_aws_session():
return boto3.Session(region_name=AWS_REGION,
aws_access_key_id=ACCESS_KEY_ID,
aws_secret_access_key=ACCESS_SECRET_KEY)
def test2():
session = init_aws_session()
s3 = session.client('s3')
res = s3.list_objects_v2(Bucket=BUCKET_NAME)
return [create_presigned_url(bucket_name=BUCKET_NAME, object_name=obj_key) for obj_key in [obj['Key'] for obj in res['Contents']]]
아래와 같은 리스트가 리턴된다.
리턴된 presigned url 예시
https://test-1219moonpasha.s3.amazonaws.com/testimage.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAW2ZRGBEP352EIVE5%2F20240102%2Feu-south-2%2Fs3%2Faws4_request&X-Amz-Date=20240102T151219Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=35f171d1400098b6c49dab88437d7ed5b28ac575e49fb21d3b1ccf79bf368ef2
확인해보니 IllegalLocationConstraintException 에러가 나면서 접근이 안된다.
검색에 들어간다.
검색 해보니 문제 원인은 아래 중 하나일 것 같다.
1. 권한 문제
2. boto3 클라이언트의 리전과 S3 버킷의 리전이 달라서 생기는 문제.
이번 경우는 2번이었다.
나의 로직에서는 init_aws_session() 함수를 사용할 때 리전을 특정해준다. 그래서 리전 문제는 아닌줄 알아서 약간 헤맸다. 알고보니 AWS에서 제공해 준 create_presigned_url() 을 그대로 가져다 쓰다 보니, boto3 클라이언트를 두번 사용했고, 두번째 클라이언트 사용 시에 리전이 특정이 안됐었다.
코드 수정.
def create_presigned_url(bucket_name, object_name, expiration=3600):
"""Generate a presigned URL to share an S3 object
:param bucket_name: string
:param object_name: string
:param expiration: Time in seconds for the presigned URL to remain valid
:return: Presigned URL as string. If error, returns None.
"""
# Generate a presigned URL for the S3 object
session = init_aws_session()
s3 = session.client('s3', region_name=AWS_REGION, endpoint_url=S3_ENDPOINT_URL)
try:
response = s3.generate_presigned_url('get_object',
Params={'Bucket': bucket_name,
'Key': object_name},
ExpiresIn=expiration)
except ClientError as e:
logging.error(e)
return None
# The response contains the presigned URL
return response
다시 생성된 presigned url 확인해보니 정상 작동된다.
https://s3.eu-south-2.amazonaws.com/test-1219moonpasha/testimage.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAW2ZRGBEP352EIVE5%2F20240102%2Feu-south-2%2Fs3%2Faws4_request&X-Amz-Date=20240102T151001Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=c466cf5350a06477673c9d0e6cdd2c5f47d29ecae013df958e7a1251b7e04e98
반환된 url을 확인해본다. 다행히 파라미터는 같았지만 요청하는 url이 달랐다.
제대로 만들어진 url은
s3.{region}.amazonaws.com/{bucket_name}/{object_key}
형식이다.
끝