투케이2K

654. (ios/swift5) [Soto 7.2.0] AWS STS 임시 정보 사용해 S3 특정 파일 다운로드 Get PreSignedUrl 확인 본문

IOS

654. (ios/swift5) [Soto 7.2.0] AWS STS 임시 정보 사용해 S3 특정 파일 다운로드 Get PreSignedUrl 확인

투케이2K 2025. 7. 24. 20:14
728x90
반응형

[개발 환경 설정]

개발 툴 : XCODE

개발 언어 : SWIFT5

 

[소스 코드]

// --------------------------------------------------------------------------------------
[개발 및 테스트 환경]
// --------------------------------------------------------------------------------------

- 언어 : Swift5

- 개발 툴 : Xcode

- 기술 구분 : Soto / AWS / S3

// --------------------------------------------------------------------------------------






// --------------------------------------------------------------------------------------
[소스 코드]
// --------------------------------------------------------------------------------------

import Foundation
import UIKit

import SotoCore
import SotoS3
import NIO
import NIOCore

class C_Aws_S3_Storage_Module: NSObject {
    
    
    // -----------------------------------------------------------------------------------------
    // MARK: - [전역 변수 선언]
    // -----------------------------------------------------------------------------------------
    public static let ACTIVITY_NAME = "C_Aws_S3_Storage_Module"
    
    public static let AWS_IAM_CLI_ACCESS_KEY = "AK..7Q"; 

    public static let AWS_IAM_CLI_SECRET_KEY = "Zz..xj"; 
    
    public static let AWS_IAM_CLI_REGION = "ap-northeast-2"; // TODO [서울 리전]
    
    public static let AWS_BUCKET_NAME = "service"; // TODO [버킷 이름]
    
    public static let AWS_BUCKET_KEY = "control/private.txt"; // TODO [버킷 Key]
    
    
    
    
    
    
    // -----------------------------------------------------------------------------------------
    // MARK: - [SEARCH FAST] : getS3StsGetPreSignedUrlCreate : S3 STS 임시 정보 사용해 특정 파일 다운로드 Get PreSignedUrl 확인
    // -----------------------------------------------------------------------------------------
    // TODO [Call Method]
    // ------------------------------------------------------------------------------------------
    /*
     C_Aws_S3_Storage_Module().getS3StsGetPreSignedUrlCreate(accessKey: C_Aws_S3_Storage_Module.AWS_IAM_CLI_ACCESS_KEY, secretKey: C_Aws_S3_Storage_Module.AWS_IAM_CLI_SECRET_KEY, region: C_Aws_S3_Storage_Module.AWS_IAM_CLI_REGION, bucketName: C_Aws_S3_Storage_Module.AWS_BUCKET_NAME, bucketKey: C_Aws_S3_Storage_Module.AWS_BUCKET_KEY, expiredHour: 1){(result) in
             
             S_Log._F_(description: "AWS S3 STS 임시 정보 사용해 특정 파일 다운로드 URL 확인", data: ["\(result)"])
             
     }
    */
    // ------------------------------------------------------------------------------------------
    static var s3GetStsPresignedUrl = ""
    func getS3StsGetPreSignedUrlCreate(accessKey: String, secretKey: String, region: String, bucketName: String, bucketKey: String, expiredHour:Int, completion: @escaping (String)->()) {
        
        /*
        // -----------------------------------------
        [getS3StsGetPreSignedUrlCreate 메소드 설명]
        // -----------------------------------------
        1. S3 STS 임시 정보 사용해 특정 파일 다운로드 Get PreSignedUrl 확인
        // -----------------------------------------
        2. 필요 import : package(url: "https://github.com/soto-project/soto.git", from: "7.2.0")
         
         import SotoCore
         import SotoS3
         import SotoSTS
        // -----------------------------------------
        3. 참고 사항 :
         
         AWS IAM 계정의 AccessKeyId 와 SecretAccessKey 를 가지고 있어야합니다

         해당 계정이 sts:AssumeRole 권한을 가지고 있어야합니다
        // -----------------------------------------
        */


        // [변수 초기화]
        C_Aws_S3_Storage_Module.s3GetStsPresignedUrl = ""

        
        // [로직 처리 수행]
        DispatchQueue.main.async {
            
            Task { // MARK: await used
                
                S_Log._D_(description: "S3 STS 임시 정보 사용해 특정 파일 다운로드 Get PreSignedUrl 생성 요청", data: [
                    "accessKey :: \(accessKey)",
                    "secretKey :: \(secretKey)",
                    "region :: \(region)",
                    "bucketName :: \(bucketName)",
                    "bucketKey :: \(bucketKey)",
                    "expiredHour :: \(expiredHour)"
                ])
                
                
                // [방어 로직 : input data check]
                if C_Util().stringNotNullMulti(data: [accessKey, secretKey, region, bucketName, bucketKey]) == true {
                    
                    // ---------------------------------------------
                    // MARK: 자격 증명 직접 입력 (정적 방식)
                    // ---------------------------------------------
                    let client = AWSClient(
                        credentialProvider: .static(
                            accessKeyId: accessKey,
                            secretAccessKey: secretKey
                        )
                        //, httpClientProvider: .createNew // SDK 6.8.0 이하 버전 설정 필요
                    )
                    
                    var stsClient: AWSClient? = nil
                    
                    do {
                        
                        // ---------------------------------------------
                        // MARK: STS 클라이언트 생성

                        // ---------------------------------------------
                        let sts = STS(client: client, region: Region(rawValue: region))
                        
                        
                        // ---------------------------------------------
                        // MARK: getSessionToken API 호출 (만료 시간 3600 = 1시간)
                        // ---------------------------------------------
                        let expiredSecond = (expiredHour * 3600)
                        let response = try await sts.getSessionToken(.init(durationSeconds: expiredSecond))
                        
                        
                        // ---------------------------------------------
                        // MARK: STS credentials 정보 확인
                        // ---------------------------------------------
                        if let creds = response.credentials {
                            S_Log._W_(description: C_Aws_S3_Storage_Module.ACTIVITY_NAME + " :: getS3StsGetPreSignedUrlCreate :: Get Sts Info Success", data: [
                                
                                "AccessKeyId :: \(creds.accessKeyId)",
                                "SecretAccessKey :: \(creds.secretAccessKey)",
                                "SessionToken :: \(creds.sessionToken)",
                                "Expiration :: \(creds.expiration)"
                            ] )
                            
                            
                            // ---------------------------------------------
                            // MARK: S3 서비스 인스턴스 생성 : STS 클라이언트 지정
                            // ---------------------------------------------
                            stsClient = AWSClient(
                                credentialProvider: .static(
                                    accessKeyId: creds.accessKeyId,
                                    secretAccessKey: creds.secretAccessKey,
                                    sessionToken: creds.sessionToken
                                )
                                //, httpClientProvider: .createNew // SDK 6.8.0 이하 버전 설정 필요
                            )
                            let s3 = S3(client: stsClient!, region: Region(rawValue: region))
                            
                            
                            // ---------------------------------------------
                            // MARK: presigned URL 생성 (.GET 방식)
                            // ---------------------------------------------
                            let url = try await s3.signURL(
                                url: URL(string: "https://\(bucketName).s3.\(region).amazonaws.com/\(bucketKey)")!,
                                httpMethod: .GET, // MARK: 파일 다운로드 Get / 파일 업로드 Put
                                expires: .hours(Int64(expiredHour))
                            )
                            
                            S_Log._W_(description: C_Aws_S3_Storage_Module.ACTIVITY_NAME + " :: getS3StsGetPreSignedUrlCreate :: Http End", data: nil )
                            
                            
                            // ---------------------------------------------
                            // MARK: URL 에러 체크 수행
                            // ---------------------------------------------
                            let urlString = url.absoluteString
                            
                            /**
                             * TODO AWS PreSignedUrl QueryParams 정리 :
                             *
                             *   >> X-Amz-Algorithm : 서명 버전과 알고리즘을 식별하고 서명을 계산하는데 사용되는 값
                             *
                             *   >> X-Amz-Credential : AccessKey, 요청 날짜, Region, 서비스 명칭
                             *
                             *   >> X-Amz-Date : ISO 8601 형식의 날짜로 URL 생성 시간
                             *
                             *   >> X-Amz-Expires : URL 유효 시간 (단위는 초)
                             *
                             *   >> X-Amz-Signature : 요청을 인증하기 위한 서명 값
                             *
                             *   >> X-Amz-SignedHeaders : 서명을 계산하기 위해 요구 되는 헤더 목록 (기본 host 헤더 요구)
                             *
                             *   >> X-Amz-Security-Token : STS 임시 정보 토큰
                             * */
                            
                            if C_Util().stringNotNull(str: urlString) == true
                                && urlString.contains("X-Amz-Credential") == true {
                             
                                // [Return Add Value]
                                C_Aws_S3_Storage_Module.s3GetStsPresignedUrl = urlString
                                
                                S_Log._W_(description: C_Aws_S3_Storage_Module.ACTIVITY_NAME + " :: getS3StsGetPreSignedUrlCreate :: Success", data: [ C_Aws_S3_Storage_Module.s3GetStsPresignedUrl ] )
                                
                                completion(C_Aws_S3_Storage_Module.s3GetStsPresignedUrl) // [Return CallBack]
                            }
                            
                            else {
                                S_Log._E_(description: C_Aws_S3_Storage_Module.ACTIVITY_NAME + " :: getS3StsGetPreSignedUrlCreate :: Error", data: [ "URL Is Null Or Type Error" ] )
                                
                                completion(C_Aws_S3_Storage_Module.s3GetStsPresignedUrl) // [Return CallBack]
                            }
                            
                            
                        } else {
                            S_Log._E_(description: C_Aws_S3_Storage_Module.ACTIVITY_NAME + " :: getS3StsGetPreSignedUrlCreate :: Error", data: [ "Get Sts Info No credentials Returned" ] )
                            
                            completion(C_Aws_S3_Storage_Module.s3GetStsPresignedUrl) // [Return CallBack]
                        }
                        
                    } catch {
                        S_Log._E_(description: C_Aws_S3_Storage_Module.ACTIVITY_NAME + " :: getS3StsGetPreSignedUrlCreate :: Exception", data: [ "\(error)" ] )
                        
                        // ---------------------------------------------
                        // MARK: PermanentRedirect: The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.
                        // ---------------------------------------------
                        // MARK: AWSClient 초기화 액세스 키 정보가 올바르지 않거나, region 에 버킷이 없는 경우 에러 발생
                        // ---------------------------------------------
                        
                        completion(C_Aws_S3_Storage_Module.s3GetStsPresignedUrl) // [Return CallBack]
                        
                    }
                    
                    // ---------------------------------------------
                    // MARK: AWSClient 리소스 정리 반드시 필요 (SotoCore/AWSClient.swift:105: Assertion failed: AWSClient not shut down before the deinit. Please call client.syncShutdown() when no longer needed.)
                    // ---------------------------------------------
                    try await client.shutdown()
                    
                    if stsClient != nil {
                        try await stsClient!.shutdown()
                    }
                }
                else {
                    S_Log._E_(description: C_Aws_S3_Storage_Module.ACTIVITY_NAME + " :: getS3StsGetPreSignedUrlCreate :: Error", data: [ "Input Data Is Null" ] )
                    
                    completion(C_Aws_S3_Storage_Module.s3GetStsPresignedUrl) // [Return CallBack]
                    
                }
                
            }
        }
    }
    
    
} // [클래스 종료]

// --------------------------------------------------------------------------------------





// --------------------------------------------------------------------------------------
[참고 사이트]
// --------------------------------------------------------------------------------------

[Soto] SPM 사용해 Soto 라이브러리 ( AWS 기능 사용 ) 설치 방법 정리

https://blog.naver.com/kkh0977/223922887762


[간단 설명] SPM (Swift Package Manager) 사용해 특정 라이브러리 버전 변경 및 업데이트 방법

https://blog.naver.com/kkh0977/223600587894?trackingCode=blog_bloghome_searchlist


[Soto Git 다운로드 주소]

https://github.com/soto-project/soto.git


[Soto 라이브러리 release 버전 참고]

https://github.com/soto-project/soto/releases?page=1

// --------------------------------------------------------------------------------------
 
728x90
반응형
Comments