-
Notifications
You must be signed in to change notification settings - Fork 1
refactor: oauth2 resource server 도입 #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cb62c8b
3ddb18d
5bbeea2
3b23808
b84df13
0ce56dc
d383ab5
a0db0ab
eee7992
18e92db
ba04fe4
6b5ba94
b641c5b
55d3424
883ba5d
76154b2
974930b
28ce8ca
fe39ccf
5244be4
baae034
3f300f6
496d3b7
b322869
f4ed5e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,83 @@ | ||||||
| package org.yapp.gateway.config | ||||||
|
|
||||||
| import com.nimbusds.jose.jwk.JWKSet | ||||||
| import com.nimbusds.jose.jwk.OctetSequenceKey | ||||||
| import com.nimbusds.jose.jwk.source.ImmutableJWKSet | ||||||
| import com.nimbusds.jose.jwk.source.JWKSource | ||||||
| import com.nimbusds.jose.proc.SecurityContext | ||||||
| import org.springframework.beans.factory.annotation.Value | ||||||
| import org.springframework.context.annotation.Bean | ||||||
| import org.springframework.context.annotation.Configuration | ||||||
| import org.springframework.security.core.authority.SimpleGrantedAuthority | ||||||
| import org.springframework.security.oauth2.jose.jws.MacAlgorithm | ||||||
| import org.springframework.security.oauth2.jwt.* | ||||||
| import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter | ||||||
| import org.yapp.gateway.constants.JwtConstants | ||||||
| import javax.crypto.spec.SecretKeySpec | ||||||
|
|
||||||
| @Configuration | ||||||
| class JwtConfig( | ||||||
| @Value("\${jwt.secret-key}") | ||||||
| private val secretKey: String | ||||||
| ) { | ||||||
| companion object { | ||||||
| private val SIGNATURE_ALGORITHM = MacAlgorithm.HS256 | ||||||
| private const val PRINCIPAL_CLAIM = "sub" | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * JWT를 암호화하고 서명하는 데 사용될 키의 소스(`JWKSource`)를 생성하여 Bean으로 등록합니다. | ||||||
| * HMAC-SHA 알고리즘에 사용될 대칭 키를 `OctetSequenceKey` 형태로 래핑하여 제공합니다. | ||||||
| * | ||||||
| * @return 생성된 `JWKSource<SecurityContext>` 객체 | ||||||
| */ | ||||||
| @Bean | ||||||
| fun jwkSource(): JWKSource<SecurityContext> { | ||||||
| val jwk: OctetSequenceKey = OctetSequenceKey.Builder(secretKey.toByteArray()).build() | ||||||
| return ImmutableJWKSet(JWKSet(jwk)) | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * JWT를 생성(인코딩)하는 `JwtEncoder`를 생성하여 Bean으로 등록합니다. | ||||||
| * 주입받은 `JWKSource`를 사용하여 토큰에 디지털 서명을 수행합니다. | ||||||
| * | ||||||
| * @param jwkSource 토큰 서명에 사용될 키 소스 | ||||||
| * @return 생성된 `JwtEncoder` 객체 | ||||||
| */ | ||||||
| @Bean | ||||||
| fun jwtEncoder(jwkSource: JWKSource<SecurityContext>): JwtEncoder { | ||||||
| return NimbusJwtEncoder(jwkSource) | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * 클라이언트로부터 받은 JWT를 검증(디코딩)하는 `JwtDecoder`를 생성하여 Bean으로 등록합니다. | ||||||
| * 토큰의 서명, 만료 시간(기본 검증) 및 발급자(issuer)가 일치하는지 확인합니다. | ||||||
| * | ||||||
| * @return 생성된 `JwtDecoder` 객체 | ||||||
| */ | ||||||
| @Bean | ||||||
| fun jwtDecoder(): JwtDecoder { | ||||||
| val secretKeySpec = SecretKeySpec(secretKey.toByteArray(), SIGNATURE_ALGORITHM.name) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 일관된 문자 인코딩을 사용하세요. JWT 인코딩과 디코딩에서 동일한 인코딩을 사용해야 합니다. UTF-8 인코딩을 명시적으로 지정하세요: - val secretKeySpec = SecretKeySpec(secretKey.toByteArray(), SIGNATURE_ALGORITHM.name)
+ val secretKeySpec = SecretKeySpec(secretKey.toByteArray(Charsets.UTF_8), SIGNATURE_ALGORITHM.name)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| val decoder = NimbusJwtDecoder.withSecretKey(secretKeySpec).build() | ||||||
| val validator = JwtValidators.createDefaultWithIssuer(JwtConstants.ISSUER) | ||||||
| decoder.setJwtValidator(validator) | ||||||
| return decoder | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * 유효성이 검증된 JWT를 Spring Security의 `Authentication` 객체로 변환하는 `JwtAuthenticationConverter`를 등록합니다. | ||||||
| * JWT의 'roles' 클레임을 애플리케이션의 권한 정보(`GrantedAuthority`)로 매핑하고, 'sub' 클레임을 사용자의 주체(Principal)로 설정합니다. | ||||||
| * | ||||||
| * @return 생성된 `JwtAuthenticationConverter` 객체 | ||||||
| */ | ||||||
| @Bean | ||||||
| fun jwtAuthenticationConverter(): JwtAuthenticationConverter { | ||||||
| val converter = JwtAuthenticationConverter() | ||||||
| converter.setJwtGrantedAuthoritiesConverter { jwt -> | ||||||
| val roles = jwt.getClaimAsStringList(JwtConstants.ROLES_CLAIM) ?: emptyList() | ||||||
| roles.map { role -> SimpleGrantedAuthority(role) } | ||||||
| } | ||||||
| converter.setPrincipalClaimName(PRINCIPAL_CLAIM) | ||||||
| return converter | ||||||
| } | ||||||
| } | ||||||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package org.yapp.gateway.constants | ||
|
|
||
| object JwtConstants { | ||
| const val ROLES_CLAIM = "roles" | ||
| const val ISSUER = "gateway" | ||
| } |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
명시적 문자 인코딩을 사용하세요.
toByteArray()는 플랫폼 기본 인코딩을 사용하므로 환경에 따라 다른 결과를 생성할 수 있습니다.명시적으로 UTF-8 인코딩을 지정하세요:
🤖 Prompt for AI Agents