generated from pricelees/issue-pr-template
[#34] 회원 / 인증 도메인 재정의 #43
@ -6,10 +6,14 @@ import io.jsonwebtoken.Claims
|
||||
import io.jsonwebtoken.ExpiredJwtException
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import org.slf4j.MDC
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.stereotype.Component
|
||||
import roomescape.auth.business.CLAIM_TYPE_KEY
|
||||
import roomescape.auth.exception.AuthErrorCode
|
||||
import roomescape.auth.exception.AuthException
|
||||
import roomescape.common.dto.MDC_PRINCIPAL_ID_KEY
|
||||
import roomescape.common.dto.PrincipalType
|
||||
import java.util.*
|
||||
import javax.crypto.SecretKey
|
||||
|
||||
@ -43,25 +47,40 @@ class JwtUtils(
|
||||
}
|
||||
}
|
||||
|
||||
fun extractIdAndType(token: String?): Pair<Long, PrincipalType> {
|
||||
val id: Long = extractSubject(token)
|
||||
.also { MDC.put(MDC_PRINCIPAL_ID_KEY, it) }
|
||||
.toLong()
|
||||
|
||||
val type: PrincipalType = extractClaim(token, CLAIM_TYPE_KEY)
|
||||
?.let { PrincipalType.valueOf(it) }
|
||||
?: run {
|
||||
log.info { "[JwtUtils.extractIdAndType] 회원 타입 조회 실패. id=$id" }
|
||||
throw AuthException(AuthErrorCode.MEMBER_NOT_FOUND)
|
||||
}
|
||||
|
||||
return id to type
|
||||
}
|
||||
|
||||
fun extractSubject(token: String?): String {
|
||||
if (token.isNullOrBlank()) {
|
||||
throw AuthException(AuthErrorCode.TOKEN_NOT_FOUND)
|
||||
}
|
||||
val claims = extractAllClaims(token)
|
||||
|
||||
return claims.subject ?: throw AuthException(AuthErrorCode.INVALID_TOKEN)
|
||||
return claims.subject ?: run {
|
||||
log.info { "[JwtUtils.extractSubject] subject를 찾을 수 없음.: token = ${token}" }
|
||||
throw AuthException(AuthErrorCode.INVALID_TOKEN)
|
||||
}
|
||||
}
|
||||
|
||||
fun extractClaim(token: String?, key: String): String {
|
||||
fun extractClaim(token: String?, key: String): String? {
|
||||
if (token.isNullOrBlank()) {
|
||||
throw AuthException(AuthErrorCode.TOKEN_NOT_FOUND)
|
||||
}
|
||||
val claims = extractAllClaims(token)
|
||||
|
||||
return claims.get(key, String::class.java) ?: run {
|
||||
log.warn { "[JwtUtils] Claim 조회 실패: key=$key" }
|
||||
throw AuthException(AuthErrorCode.INVALID_TOKEN)
|
||||
}
|
||||
return claims.get(key, String::class.java)
|
||||
}
|
||||
|
||||
private fun extractAllClaims(token: String): Claims {
|
||||
|
||||
@ -4,7 +4,6 @@ import io.github.oshai.kotlinlogging.KLogger
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.slf4j.MDC
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.web.method.HandlerMethod
|
||||
import org.springframework.web.servlet.HandlerInterceptor
|
||||
@ -14,8 +13,8 @@ import roomescape.auth.exception.AuthErrorCode
|
||||
import roomescape.auth.exception.AuthException
|
||||
import roomescape.auth.infrastructure.jwt.JwtUtils
|
||||
import roomescape.auth.web.support.AdminOnly
|
||||
import roomescape.auth.web.support.MDC_MEMBER_ID_KEY
|
||||
import roomescape.auth.web.support.accessToken
|
||||
import roomescape.common.dto.PrincipalType
|
||||
|
||||
private val log: KLogger = KotlinLogging.logger {}
|
||||
|
||||
@ -35,20 +34,28 @@ class AdminInterceptor(
|
||||
val annotation: AdminOnly = handler.getMethodAnnotation(AdminOnly::class.java) ?: return true
|
||||
|
||||
val token: String? = request.accessToken()
|
||||
val adminId = jwtUtils.extractSubject(token).also { MDC.put(MDC_MEMBER_ID_KEY, it) }
|
||||
val (id, type) = jwtUtils.extractIdAndType(token)
|
||||
|
||||
jwtUtils.extractClaim(
|
||||
token = token, key = CLAIM_PERMISSION_KEY
|
||||
).also {
|
||||
val permission = AdminPermissionLevel.valueOf(it)
|
||||
|
||||
if (!permission.hasPrivilege(annotation.privilege)) {
|
||||
log.warn { "[AuthInterceptor] 관리자 권한 부족: required=${annotation.privilege} / current=${permission}" }
|
||||
val permission: AdminPermissionLevel = jwtUtils.extractClaim(token, key = CLAIM_PERMISSION_KEY)
|
||||
?.let {
|
||||
AdminPermissionLevel.valueOf(it)
|
||||
}
|
||||
?: run {
|
||||
if (type != PrincipalType.ADMIN) {
|
||||
log.warn { "[AdminInterceptor] 회원의 관리자 API 접근: id=${id}" }
|
||||
throw AuthException(AuthErrorCode.ACCESS_DENIED)
|
||||
}
|
||||
log.info { "[AuthInterceptor] 인증 완료. adminId=$adminId, permission=${permission}" }
|
||||
log.warn { "[AdminInterceptor] 토큰에서 이용자 권한이 조회되지 않음: id=${id}" }
|
||||
throw AuthException(AuthErrorCode.MEMBER_NOT_FOUND)
|
||||
}
|
||||
|
||||
if (!permission.hasPrivilege(annotation.privilege)) {
|
||||
log.warn { "[AdminInterceptor] 관리자 권한 부족: required=${annotation.privilege} / current=${permission}" }
|
||||
throw AuthException(AuthErrorCode.ACCESS_DENIED)
|
||||
}
|
||||
|
||||
log.info { "[AdminInterceptor] 인증 완료. adminId=$id, permission=${permission}" }
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,17 +4,13 @@ import io.github.oshai.kotlinlogging.KLogger
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.slf4j.MDC
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.web.method.HandlerMethod
|
||||
import org.springframework.web.servlet.HandlerInterceptor
|
||||
import roomescape.auth.business.AuthServiceV2
|
||||
import roomescape.auth.business.CLAIM_TYPE_KEY
|
||||
import roomescape.auth.infrastructure.jwt.JwtUtils
|
||||
import roomescape.auth.web.support.Authenticated
|
||||
import roomescape.auth.web.support.MDC_MEMBER_ID_KEY
|
||||
import roomescape.auth.web.support.accessToken
|
||||
import roomescape.common.dto.PrincipalType
|
||||
|
||||
private val log: KLogger = KotlinLogging.logger {}
|
||||
|
||||
@ -34,12 +30,10 @@ class AuthenticatedInterceptor(
|
||||
}
|
||||
|
||||
val token: String? = request.accessToken()
|
||||
|
||||
val id = jwtUtils.extractSubject(token).also { MDC.put(MDC_MEMBER_ID_KEY, it) }
|
||||
val type = jwtUtils.extractClaim(token, CLAIM_TYPE_KEY)
|
||||
val (id, type) = jwtUtils.extractIdAndType(token)
|
||||
|
||||
try {
|
||||
authService.findContextById(id.toLong(), PrincipalType.valueOf(type))
|
||||
authService.findContextById(id, type)
|
||||
log.info { "[AuthenticatedInterceptor] 인증 완료. id=$id, type=${type}" }
|
||||
|
||||
return true
|
||||
|
||||
@ -4,15 +4,12 @@ import io.github.oshai.kotlinlogging.KLogger
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.slf4j.MDC
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.web.method.HandlerMethod
|
||||
import org.springframework.web.servlet.HandlerInterceptor
|
||||
import roomescape.auth.business.CLAIM_TYPE_KEY
|
||||
import roomescape.auth.exception.AuthErrorCode
|
||||
import roomescape.auth.exception.AuthException
|
||||
import roomescape.auth.infrastructure.jwt.JwtUtils
|
||||
import roomescape.auth.web.support.MDC_MEMBER_ID_KEY
|
||||
import roomescape.auth.web.support.UserOnly
|
||||
import roomescape.auth.web.support.accessToken
|
||||
import roomescape.common.dto.PrincipalType
|
||||
@ -34,17 +31,14 @@ class UserInterceptor(
|
||||
}
|
||||
|
||||
val token: String? = request.accessToken()
|
||||
val userId = jwtUtils.extractSubject(token).also { id -> MDC.put(MDC_MEMBER_ID_KEY, id) }
|
||||
val (id, type) = jwtUtils.extractIdAndType(token)
|
||||
|
||||
jwtUtils.extractClaim(token, CLAIM_TYPE_KEY).also {
|
||||
if (it != PrincipalType.USER.name) {
|
||||
log.warn { "[UserInterceptor] 관리자의 회원 API 접근: id=${userId}" }
|
||||
if (type != PrincipalType.USER) {
|
||||
log.warn { "[UserInterceptor] 관리자의 회원 API 접근: id=${id}" }
|
||||
throw AuthException(AuthErrorCode.ACCESS_DENIED)
|
||||
}
|
||||
}
|
||||
|
||||
log.info { "[AuthInterceptor] 인증 완료. userId=$userId" }
|
||||
|
||||
log.info { "[UserInterceptor] 인증 완료. userId=$id" }
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,13 +10,11 @@ import org.springframework.web.context.request.NativeWebRequest
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver
|
||||
import org.springframework.web.method.support.ModelAndViewContainer
|
||||
import roomescape.auth.business.AuthServiceV2
|
||||
import roomescape.auth.business.CLAIM_TYPE_KEY
|
||||
import roomescape.auth.exception.AuthErrorCode
|
||||
import roomescape.auth.exception.AuthException
|
||||
import roomescape.auth.infrastructure.jwt.JwtUtils
|
||||
import roomescape.auth.web.support.CurrentUser
|
||||
import roomescape.auth.web.support.accessToken
|
||||
import roomescape.common.dto.PrincipalType
|
||||
|
||||
private val log: KLogger = KotlinLogging.logger {}
|
||||
|
||||
@ -40,14 +38,12 @@ class CurrentUserContextResolver(
|
||||
val token: String? = request.accessToken()
|
||||
|
||||
try {
|
||||
val id: String = jwtUtils.extractSubject(token)
|
||||
val type: PrincipalType = PrincipalType.valueOf(jwtUtils.extractClaim(token, CLAIM_TYPE_KEY))
|
||||
val (id, type) = jwtUtils.extractIdAndType(token)
|
||||
|
||||
return authService.findContextById(id.toLong(), type)
|
||||
return authService.findContextById(id, type)
|
||||
} catch (e: Exception) {
|
||||
log.info { "[MemberIdResolver] 회원 조회 실패. message=${e.message}" }
|
||||
throw AuthException(AuthErrorCode.MEMBER_NOT_FOUND)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user