Back End/AWS

[AWS] CloudFront SignedCookie 특정경로 하위 모두 적용하기 (Java, Kotlin)

DevPing9_ 2023. 5. 1. 18:10

서론

Node.js 에 대한 레퍼런스는 넘치는데에 비해 Java 에 대한 레퍼런스가 거의 없었다.

GPT 에게 물어봐도 JS 레퍼런스 코드를 Java 형식으로 변환해놓은 듯한 괴랄한 코드를 주었다... (이게 편항된 학습인 것인가...)

CookiesSigner 같은 생성자는 없었단 말이다..

 

구글링해서 나온 JS 예제코드가 GPT 가 만들어준 Java 코드와 미칠듯이 흡사하다.


 

Signed Cookie 목적

URI 에 대한 접근권한을 제어하고 싶을 때 사용한다.


보통 S3 를 Cloudfront 로 배포하여 서빙하게 되는데, CloudFront(S3)의 특정 URL 에 대해 Signed Cookie 를 획득한 사용자만 접근가능하도록 할 수 있다.

 


 

작업 목록

1. AWS SDK 라이브러리를 찾는다. 

2. Cloudfront 와 비대칭키를 나눠 갖는다.

3. SignedCookie 를 만드는 녀석을 찾아 비지니스 로직 이후에 SignedCookie 를 Response 에 세팅한다.

 

심플하네! 금방 작업하겠군! 🐧

 


 

끊임 없는 AccessDenied

ㅎㅎㅎㅎ 으아ㅏㅇ아ㅏ

 

찾은 레퍼런스라곤 Java Doc 하나 였는데 이 괴상망측한 Doc 을 읽으며

CloudFrontCookieSigner.getCookiesForCannedPolicy() 예제를 따라해보면 동작하긴 한다.

단일 URL한정해서 말이다.

 

나는 특정 경로의 하위 리소스를 모두 허락 해주고 싶은데 말이다.

`/item/103/*` 가능하게 해달라고오오오오!!


 

솔루션 먼저


아래는 Kotlin 에서 작성한 특정 경로 하위 리소스를 모두 허락 해주는 Signed Cookie 샘플코드이다. 

라이브러리를 까보면 함수 인자에 대한 설명이 매우매우 부실하여 주석으로 예시를 적어두었다.

fun getBookContentResponse(
    req: HttpServletRequest,
    res: HttpServletResponse,
    bookContentId: String
): BookContentResponse {

    val expireCalendar = Calendar.getInstance()
    expireCalendar.add(Calendar.MINUTE, 60)

    val resourcePath = "$bookContentId/*"

    val privateKeyFile = File(privateKeyLocation)

    val cookies = CloudFrontCookieSigner
        .getCookiesForCustomPolicy(
            SignerUtils.Protocol.https, // e.g. "https"
            contentCloudFrontFQDN, // e.g. "www.abc.com"
            privateKeyFile, // CloudFront 에 등록된 공개키와 매칭되는 비밀키
            resourcePath, // e.g. "image/*"
            cfPubKey, // CloudFront 에 등록된 공개키
            expireCalendar.time, // Signed Cookie 만료시간
            null, // Signed Cookie 사용가능 시작시간, null 일 경우 쿠키 받자마자 사용가능
            null // Signed Cookie 허용 IP, null 일 경우 제한 없음
        )

    val url = SignerUtils.generateResourcePath(SignerUtils.Protocol.https, contentCloudFrontFQDN, resourcePath)

    res.addCookie(makeSignedCookie(cookies.getPolicy().key, cookies.getPolicy().value))
    res.addCookie(makeSignedCookie(cookies.signature.key, cookies.signature.value))
    res.addCookie(makeSignedCookie(cookies.keyPairId.key, cookies.keyPairId.value))

    return BookContentResponse(baseUrl = url.substring(0, url.length - 2))
}

fun makeSignedCookie(key: String, value: String): Cookie {
        val cookie = Cookie(key, value)

        cookie.domain = getRootDomain(contentCloudFrontFQDN) // you can customize
        cookie.path = "/"
        cookie.isHttpOnly = true
        cookie.secure = true
        return cookie
}

 

 

CannedPolicy 는 `item/103/*` 식의 resourcePath 설정이 먹히지 않는다.

CannedPolicy 와 CustomPolicy 의 차이점은 쿠키이름에 policy 를 설정하느냐 expires 를 설정하느냐인데

이 차이점이 resourcePath 에 관한 정책에 영향을 주는 듯 하다. (AWS Statement 는 동일한데 말이지..)

 

굳이 테스트해서 안되는 이유가 무엇인지 명확히 찾기에는 투자대비 효용이 없다 판단하여 진행하지 않았다.

플랫폼 사용법이나 정책은 바뀌기 마련이니까.. 또 deprecated notice 메일 날라오겠지...

 


 

하소연 할거야!!


1. resourcePath

AWS 공식문서 또는 라이브러리 코드를 보면  resourcePath 가 `/` 로 시작하면 안된다고 명시되어있지 않다.

좀 적어놓지 그랬니 ^ㅡ^ 이걸로 시간을 얼마나 날린거야!!!!

2. activeFrom, ipRange

null 이면 AWS Statement 에 포함시키지 않는다.

null 처리를 이렇게 한다고?

 

3. protocol + distributionDomain + resourcePath = url ?

너무한 거 아니냐고~!~! 

domain 이 root domain 이 아니고 FQDN 인건 Java Doc 주석에라도 좀 명시해주지... 


마치며

그래도 값진 경험 하나 했다...

구글링이나 GPT 로 Getting Started 코드가 나오지 않는다면 라이브러리를 최대한 빨리 모두 까보는게 시간을 save 하는 길이란 걸...

어설프게 한 단계만 봤다간 피본다는 것을... (resourcePath 랑 domain, ipRange, activeFrom 은 아직도 어이없네..)

728x90