Kotlin / Spring Boot κΈ°λ° μΈμ¦ APIμ
λλ€. μμ
λ‘κ·ΈμΈ(Google, Kakao, Naver), μ΄λ©μΌ μΈμ¦, JWTΒ·μΏ ν€, GET /api/v1/auth/introspect λ±μ μ 곡ν©λλ€.
./gradlew bootRunκΈ°λ³Έ ν¬νΈ 9000, νλ‘νμ application.ymlμ spring.profiles κ·Έλ£Ήμ λ°λ¦
λλ€.
application.yml, application-jwt.yml, application-oauth.yml, νλ‘νλ³ application-*.ymlμ λ§€νλ©λλ€.
νμν ν€λ μ API λͺ
μΈμ μ€μ νμΌμ μ°Έκ³ νμΈμ.
ATΒ·RT λ RS256 μΌλ‘ μλͺ
ν©λλ€. κ°μΈν€λ auth-api λ§ κ°μ§κ³ , 곡κ°ν€λ GET /.well-known/jwks.json μΌλ‘ λ°°ν¬ν©λλ€.
| λ³μ | νμ | μ€λͺ |
|---|---|---|
JWT_PRIVATE_KEY |
μ | PKCS#8 RSA κ°μΈν€. PEM ν€λΒ·νΈν°Β·κ°νμ λΊ ν μ€ base64 |
JWT_SECRET |
μλμ€ | HS256 λ κ±°μ κ²μ¦ μ μ© μν¬λ¦Ώ. HS256 μΌλ‘ λ°κΈλ ν ν°μ΄ μ΄μ μλ μ ν κΈ°κ°(RT λ§λ£ κΈ°κ°)μλ§ λκ³ , μ§λλ©΄ μ§μλλ€. λΉμ΄ μμΌλ©΄ HS256 ν ν°μ κ±°λΆν©λλ€ |
JWT_ACCESS_TOKEN_EXPIRATION |
μ | μ: 30m |
JWT_REFRESH_TOKEN_EXPIRATION |
μ | μ: 7d |
λ‘컬 ν°λ―Έλμμ μ€νν©λλ€. EC2 μ λ€μ΄κ° νμκ° μκ³ , μλ²μλ ν€ νμΌμ λ¨κΈ°μ§ μμ΅λλ€.
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out jwt-private.pem
grep -v '^-----' jwt-private.pem | tr -d '\n' | pbcopy # JWT_PRIVATE_KEY κ°μ΄ ν΄λ¦½λ³΄λμ 볡μ¬λλ€genpkeyλ PKCS#8(BEGIN PRIVATE KEY)λ‘ λμ΅λλ€.openssl genrsaλ‘ λ§λ PKCS#1(BEGIN RSA PRIVATE KEY)μ μ½μ§ λͺ»νλ―λ‘openssl pkcs8 -topk8 -nocrypt -in rsa.pem -out jwt-private.pemμΌλ‘ λ³νν©λλ€.- PEM λ³Έλ¬Έμ΄ μ΄λ―Έ base64 μ΄λ―λ‘
base64λͺ λ ΉμΌλ‘ ν λ² λ κ°μΈμ§ μμ΅λλ€. λ λ² κ°μΈλ©΄ "PKCS#8 RSA κ°μΈν€κ° μλλλ€" λ‘ κΈ°λμ΄ μ€ν¨ν©λλ€. - ν μ€μ΄μ΄μΌ ν©λλ€.
.envλ μ¬λ¬ μ€ κ°μ λ΄μ§ λͺ»ν©λλ€. κ° μ λμ λ°μ΄νλ₯Ό λΆμ΄μ§ μμ΅λλ€. - 2048λΉνΈ λ―Έλ§ ν€λ κΈ°λ μ κ±°λΆλ©λλ€.
λ±λ‘: GitHub μ μ₯μ Settings β Environments β (alpha | production) β AUTH_BE_ENV_FILE μ μ΄μ΄ μλ μ€μ μΆκ°ν©λλ€. νκ²½λ§λ€ ν€λ₯Ό λ°λ‘ λ§λλλ€. λ°°ν¬ μν¬νλ‘κ° μ΄ μν¬λ¦Ώμ EC2 μ env/auth-api.env λ‘ λ³΅μ¬νλ―λ‘ μλ²μμ μλ κ²μ μμ΅λλ€.
JWT_PRIVATE_KEY=MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQD... # ν μ€
- μ²μ²©μ₯(alphaΒ·prod):
JWT_SECRETμ€μ κ·Έλλ‘ λκ³ μ΄ μ€λ§ μΆκ°ν©λλ€(μλ μ ν μ μ°¨). - lnb:
JWT_PRIVATE_KEYλ§ λ£κ³JWT_SECRETμ λ£μ§ μμ΅λλ€. - λ±λ‘μ΄ λλλ©΄ λ‘컬μ
jwt-private.pemμ μμ ν©λλ€. μ μ₯μμ 컀λ°νμ§ μμ΅λλ€. ν μ€νΈ μ½λμTestJwtKeysν€λ ν μ€νΈ μ μ©μ΄λΌ μ΄μμ μ°λ©΄ μ λ©λλ€.
ν€κ° μλͺ»λλ©΄ 컨ν
μ΄λκ° λ¨μ§ μκ³ docker logs μ jwt.private-key κ° β¦ λ‘ μμνλ μμΈμ΄ λ¨μ΅λλ€. μ μ κΈ°λνλ€λ©΄ JWKS λ‘ νμΈν©λλ€.
JWKS λ μΈλΆμμ 보μ΄μ§ μλ κ²μ΄ μ μμ
λλ€. μ²μ²©μ₯ auth-api λ API κ²μ΄νΈμ¨μ΄ λ€μ μκ³ κ²μ΄νΈμ¨μ΄μ μ΄ κ²½λ‘ λΌμ°νΈκ° μμΌλ©°, μ²μ²©μ₯ κ²½λ‘μμλ JWKS λ₯Ό μ½λ μͺ½μ΄ μμ΅λλ€(κ²μ¦μ auth-api κ° introspect μμμ ν©λλ€). lnb-api λ κ°μ docker λ€νΈμν¬μμ http://auth-api:9000/.well-known/jwks.json μ μ§μ μ½μ΅λλ€. κ·Έλμ νμΈμ μλ² μμμ ν©λλ€.
# EC2 μ ssh λ‘ λ€μ΄κ° λ€
docker exec <auth-api 컨ν
μ΄λλͺ
> wget -qO- http://localhost:9000/.well-known/jwks.jsonkeys[0].kid κ° λΉμ΄ μμ§ μκ³ n μ΄ μμΌλ©΄ μ μμ
λλ€. 컨ν
μ΄λμ wget μ΄ μμΌλ©΄ κ°μ λ€νΈμν¬μ λ€λ₯Έ 컨ν
μ΄λμμ curl http://auth-api:9000/.well-known/jwks.json μΌλ‘ λ΄
λλ€.
JWT_PRIVATE_KEY λ₯Ό μΆκ°νκ³ JWT_SECRET μ μ μ§ν μ± λ°°ν¬νλ€(μΈμ
μ μ§). JWT_REFRESH_TOKEN_EXPIRATION μ΄μ μ§λ λ€ JWT_SECRET μ μ§μ°κ³ μ¬λ°°ν¬νλ€.
μ ν μ μ μ:
JWT_SECRETμ λ무 μΌμ° μ§μ°λ©΄: μ HS256 AT λ₯Ό κ°μ§ λΈλΌμ°μ λ introspect μμ μΏ ν€κ° μ§μμ§μ§ μμ μ± 401 μ λ°κ³ , AT μΏ ν€ λ§λ£(JWT_ACCESS_TOKEN_EXPIRATION) λ€ silent refresh κ²½λ‘μμ μΏ ν€κ° μ§μμ Έ μ¬λ‘κ·ΈμΈνκ² λ©λλ€.JWT_REFRESH_TOKEN_EXPIRATIONμ΄μ κΈ°λ€λ¦¬λ©΄ μ΄ μν©μ΄ μκΈ°μ§ μμ΅λλ€.- λ‘€λ°±: RS256 μ΄λ―Έμ§μμ HS256 μ΄λ―Έμ§λ‘ λλ리면 κ·Έ μ¬μ΄ λ°κΈλ μΈμ
μ μ λΆ κΉ¨μ§λλ€. λ‘€λ°± κ°λ₯μ±μ λ¨κΈ°λ €λ©΄ μ ν μ°½ λμ
JWT_SECRETμ μ μ§ν©λλ€. - ν€ κ΅μ²΄(
JWT_PRIVATE_KEYλ³κ²½)λkidκ° λ°λμ΄ κΈ°μ‘΄ μΈμ μ μ λ‘κ·Έμμμ΄λ©°, JWKS λ₯Ό μΊμνλ μλΉμ(lnb-api)λ μ¬μμν΄μΌ ν©λλ€. νμ μ§μμ μ΄λ² λ²μ λ°μ λλ€. - ν ν° ν¬κΈ°: RS256 μλͺ
μ HS256 λ³΄λ€ μ½ 300 λ°μ΄νΈ κΉλλ€(ATΒ·RT κ°κ°). μΏ ν€Β·
X-New-AT/X-New-RTν€λ ν¬κΈ°κ° κ·Έλ§νΌ λμ΄λ©λλ€.