실무 프로젝트로 배우는 Kotlin & Spring/회원 인증 서비스 개발하기

JWT 기반 인증 구현

webmaster 2022. 12. 13. 22:49
728x90

build.gradle 

dependencies {
    // JWT 인증
    implementation("com.auth0:java-jwt:3.19.2")
}
  • 해당 의존성을 추가하여 JWT 기능을 사용하자

application.yml 

jwt:
  issuer: java #프로젝트 이름
  subject: auth
  expires-time: 3600 #1시간
  secret: my-secret
  • JWT에 관련된 설정 추가
    • issuer : 토큰을 발급한 발급자
    • subject: claim의 주제(토큰이 갖는 문맥)
    • expires-time: 만료 시간
    • secret: 비밀 키

JWTUtils

object JWTUtils {
    fun createToken(claim: JWTClaim, properties: JWTProperties) =
        JWT.create()
            .withIssuer(properties.issuer)
            .withSubject(properties.subject)
            .withIssuedAt(Date())
            .withExpiresAt(Date(Date().time + properties.expiresTime * 1000))
            .withClaim("userId", claim.userId)
            .withClaim("email", claim.email)
            .withClaim("profileUrl", claim.profileUrl)
            .withClaim("username", claim.username)
            .sign(Algorithm.HMAC256(properties.secret))

    fun decode(token: String, secret: String, issuer: String): DecodedJWT {
        val algorithm = Algorithm.HMAC256(secret)
        val verifier = JWT.require(algorithm)
            .withIssuer(issuer)
            .build()
        return verifier.verify(token) //정상일 경우 DecodedJWT 반환
    }
}

data class JWTClaim(
    val userId: Long,
    val email: String,
    val profileUrl: String,
    val username: String,
)

JWTProperties

@ConstructorBinding //설정 클래스에 생성자에 값이 추가되게 된다.
@ConfigurationProperties(prefix = "jwt")
data class JWTProperties(
    val issuer: String,
    val subject: String,
    val expiresTime: Long,
    val secret: String,
)
  • JWTProperties에서, @ConstructorBinding을 통해 설정 클래스의 생성자를 통해 Property 값을 채워준다.
    • @ConfigurationProperties 어노테이션을 통해 yml에 있는 jwt 하위에서 읽은 값을 생성해 줄 것을 명시한다.
  • JWTUtils에서 인증 관련 처리를 진행한다
    • createToken : JWT 토큰을 생성한다.
    • decode: JWT 토큰을 decode 한다.

JWTUtilsTest

class JWTUtilsTest {
    private val logger = KotlinLogging.logger {}


    @Test
    fun createTokenTest() {
        val jwtClaim = JWTClaim(
            userId = 1,
            email = "dev@gmail.com",
            profileUrl = "profile.jpg",
            username = "개발자"
        )

        val properties = JWTProperties(
            issuer = "java",
            subject = "auth",
            expiresTime = 3600,
            secret = "my-secret"
        )

        val token = JWTUtils.createToken(jwtClaim, properties)
        assertNotNull(token)
        logger.info { "token: $token" }
    }

    @Test
    fun decodeTest() {
        val jwtClaim = JWTClaim(
            userId = 1,
            email = "dev@gmail.com",
            profileUrl = "profile.jpg",
            username = "개발자"
        )

        val properties = JWTProperties(
            issuer = "java",
            subject = "auth",
            expiresTime = 3600,
            secret = "my-secret"
        )

        val token = JWTUtils.createToken(jwtClaim, properties)
        val decode = JWTUtils.decode(token, secret = properties.secret, issuer = properties.issuer)
        with(decode) {
            logger.info { "claims: $claims" }
            val userId = claims["userId"]!!.asLong()
            assertEquals(userId, jwtClaim.userId)

            val email = claims["email"]!!.asString()
            assertEquals(email, jwtClaim.email)

            val profileUrl = claims["profileUrl"]!!.asString()
            assertEquals(profileUrl, jwtClaim.profileUrl)

            val username = claims["username"]!!.asString()
            assertEquals(username, jwtClaim.username)
        }
    }
}
  • JWT 토큰이 잘 암호화가 되는지, 복호화가 되는지 log를 통해 찍어보고, 검증한다.
728x90

'실무 프로젝트로 배우는 Kotlin & Spring > 회원 인증 서비스 개발하기' 카테고리의 다른 글

로그인과 로그아웃  (0) 2022.12.15
회원가입  (0) 2022.12.14
토큰 기반 인증  (0) 2022.12.13
공통 에러 처리  (0) 2022.12.13
API 스펙 정의  (0) 2022.12.13