-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[YS-70] feat: JWT 토큰에 사용자 권한(RoleType) 정보 포함 (#18)
* refact: delete unused file * refact: add roleName to Enum * feat: add memberRole to jwt token * test: add role validation test code * test: move usecase's test code to application package * style: delete unused import * refact: refactor exception handling to be more granular for token validation * style: rename JpaMemberRepository to MemberRepository for better clarity
- Loading branch information
Showing
14 changed files
with
126 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 4 additions & 2 deletions
6
src/main/kotlin/com/dobby/backend/domain/gateway/TokenGateway.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
package com.dobby.backend.domain.gateway | ||
|
||
import com.dobby.backend.domain.model.Member | ||
|
||
interface TokenGateway { | ||
fun generateAccessToken(memberId: Long): String | ||
fun generateRefreshToken(memberId: Long): String | ||
fun generateAccessToken(member: Member): String | ||
fun generateRefreshToken(member: Member): String | ||
fun extractMemberIdFromRefreshToken(token: String): String | ||
} |
7 changes: 5 additions & 2 deletions
7
src/main/kotlin/com/dobby/backend/infrastructure/database/entity/enum/RoleType.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
package com.dobby.backend.infrastructure.database.entity.enum | ||
|
||
enum class RoleType { | ||
RESEARCHER, PARTICIPANT | ||
enum class RoleType( | ||
val roleName: String | ||
) { | ||
RESEARCHER("RESEARCHER"), | ||
PARTICIPANT("PARTICIPANT") | ||
} |
7 changes: 0 additions & 7 deletions
7
src/main/kotlin/com/dobby/backend/infrastructure/database/repository/MemberJpaRepository.kt
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 9 additions & 5 deletions
14
src/main/kotlin/com/dobby/backend/infrastructure/gateway/TokenGatewayImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
src/test/kotlin/com/dobby/backend/application/usecase/GenerateTestTokenTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.dobby.backend.application.usecase | ||
|
||
import com.dobby.backend.domain.gateway.MemberGateway | ||
import io.kotest.core.spec.style.BehaviorSpec | ||
import com.dobby.backend.domain.gateway.TokenGateway | ||
import com.dobby.backend.domain.model.Member | ||
import com.dobby.backend.infrastructure.database.entity.enum.MemberStatus | ||
import com.dobby.backend.infrastructure.database.entity.enum.ProviderType | ||
import com.dobby.backend.infrastructure.database.entity.enum.RoleType | ||
import io.kotest.matchers.shouldBe | ||
import io.mockk.every | ||
import io.mockk.mockk | ||
import java.time.LocalDate | ||
|
||
class GenerateTestTokenTest: BehaviorSpec({ | ||
val tokenGateway = mockk<TokenGateway>() | ||
val memberGateway = mockk<MemberGateway>() | ||
val generateTestToken = GenerateTestToken(tokenGateway, memberGateway) | ||
|
||
given("memberId가 주어졌을 때") { | ||
val member = Member(memberId = 1, oauthEmail = "[email protected]", contactEmail = "[email protected]", | ||
provider = ProviderType.NAVER, role = RoleType.PARTICIPANT, name = "dobby", | ||
birthDate = LocalDate.of(2000, 7, 8), status = MemberStatus.ACTIVE) | ||
val accessToken = "testAccessToken" | ||
val refreshToken = "testRefreshToken" | ||
|
||
every { tokenGateway.generateAccessToken(member) } returns accessToken | ||
every { tokenGateway.generateRefreshToken(member) } returns refreshToken | ||
every { memberGateway.getById(1) } returns member | ||
|
||
`when`("execute가 호출되면") { | ||
val input = GenerateTestToken.Input(member.memberId) | ||
val result = generateTestToken.execute(input) | ||
|
||
then("생성된 accessToken과 refreshToken이 반환되어야 한다") { | ||
result.accessToken shouldBe accessToken | ||
result.refreshToken shouldBe refreshToken | ||
} | ||
} | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,34 @@ | ||
package com.dobby.backend.domain.usecase | ||
package com.dobby.backend.application.usecase | ||
|
||
import com.dobby.backend.application.usecase.GenerateTokenWithRefreshToken | ||
import com.dobby.backend.domain.gateway.MemberGateway | ||
import com.dobby.backend.domain.gateway.TokenGateway | ||
import com.dobby.backend.domain.model.Member | ||
import com.dobby.backend.infrastructure.database.entity.enum.MemberStatus | ||
import com.dobby.backend.infrastructure.database.entity.enum.ProviderType | ||
import com.dobby.backend.infrastructure.database.entity.enum.RoleType | ||
import io.kotest.core.spec.style.BehaviorSpec | ||
import io.kotest.matchers.shouldBe | ||
import io.mockk.every | ||
import io.mockk.mockk | ||
import java.time.LocalDate | ||
|
||
class GenerateTokenWithRefreshTokenTest : BehaviorSpec({ | ||
val tokenGateway = mockk<TokenGateway>() | ||
val generateTokenWithRefreshToken = GenerateTokenWithRefreshToken(tokenGateway) | ||
val memberGateway = mockk<MemberGateway>() | ||
val generateTokenWithRefreshToken = GenerateTokenWithRefreshToken(tokenGateway, memberGateway) | ||
|
||
given("유효한 리프레시 토큰이 주어졌을 때") { | ||
val validRefreshToken = "validRefreshToken" | ||
val memberId = 123L | ||
val member = Member(memberId = 1, oauthEmail = "[email protected]", contactEmail = "[email protected]", | ||
provider = ProviderType.NAVER, role = RoleType.PARTICIPANT, name = "dobby", | ||
birthDate = LocalDate.of(2000, 7, 8), status = MemberStatus.ACTIVE) | ||
val accessToken = "newAccessToken" | ||
val newRefreshToken = "newRefreshToken" | ||
|
||
every { tokenGateway.extractMemberIdFromRefreshToken(validRefreshToken) } returns memberId.toString() | ||
every { tokenGateway.generateAccessToken(memberId) } returns accessToken | ||
every { tokenGateway.generateRefreshToken(memberId) } returns newRefreshToken | ||
every { tokenGateway.extractMemberIdFromRefreshToken(validRefreshToken) } returns member.memberId.toString() | ||
every { tokenGateway.generateAccessToken(member) } returns accessToken | ||
every { tokenGateway.generateRefreshToken(member) } returns newRefreshToken | ||
every { memberGateway.getById(1) } returns member | ||
|
||
`when`("execute가 호출되면") { | ||
val input = GenerateTokenWithRefreshToken.Input(refreshToken = validRefreshToken) | ||
|
@@ -28,7 +37,7 @@ class GenerateTokenWithRefreshTokenTest : BehaviorSpec({ | |
then("accessToken과 refreshToken이 생성되고, memberId가 포함된다") { | ||
result.accessToken shouldBe accessToken | ||
result.refreshToken shouldBe newRefreshToken | ||
result.memberId shouldBe memberId | ||
result.memberId shouldBe member.memberId | ||
} | ||
} | ||
} | ||
|
32 changes: 0 additions & 32 deletions
32
src/test/kotlin/com/dobby/backend/domain/usecase/GenerateTestTokenTest.kt
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ import io.kotest.matchers.shouldNotBe | |
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.boot.test.context.SpringBootTest | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken | ||
import org.springframework.security.core.authority.SimpleGrantedAuthority | ||
import org.springframework.test.context.ActiveProfiles | ||
import java.time.LocalDate | ||
import kotlin.test.assertFailsWith | ||
|
@@ -27,7 +28,8 @@ class JwtTokenProviderTest : BehaviorSpec() { | |
val member = Member(memberId = 1, oauthEmail = "[email protected]", contactEmail = "[email protected]", | ||
provider = ProviderType.NAVER, role = RoleType.PARTICIPANT, name = "dobby", | ||
birthDate = LocalDate.of(2000, 7, 8), status = MemberStatus.ACTIVE) | ||
val authentication = UsernamePasswordAuthenticationToken(member.memberId, null) | ||
val authorities = listOf(SimpleGrantedAuthority(member.role?.name ?: "PARTICIPANT")) | ||
val authentication = UsernamePasswordAuthenticationToken(member.memberId, null, authorities) | ||
|
||
`when`("해당 인증 정보로 JWT 토큰을 생성하면") { | ||
val jwtToken = jwtTokenProvider.generateAccessToken(authentication) | ||
|
@@ -42,17 +44,24 @@ class JwtTokenProviderTest : BehaviorSpec() { | |
val member = Member(memberId = 1, oauthEmail = "[email protected]", contactEmail = "[email protected]", | ||
provider = ProviderType.NAVER, role = RoleType.PARTICIPANT, name = "dobby", | ||
birthDate = LocalDate.of(2000, 7, 8), status = MemberStatus.ACTIVE) | ||
val authentication = UsernamePasswordAuthenticationToken(member.memberId, null) | ||
val authorities = listOf(SimpleGrantedAuthority(member.role?.name ?: "PARTICIPANT")) | ||
val authentication = UsernamePasswordAuthenticationToken(member.memberId, null, authorities) | ||
val validToken = jwtTokenProvider.generateAccessToken(authentication) | ||
|
||
`when`("해당 토큰을 파싱하면") { | ||
val parsedAuthentication = jwtTokenProvider.parseAuthentication(validToken) | ||
val extractedMemberId = parsedAuthentication.principal | ||
val extractedAuthorities = parsedAuthentication.authorities | ||
|
||
then("파싱된 멤버의 ID는 원래 멤버의 ID와 같아야 한다") { | ||
extractedMemberId shouldNotBe null | ||
extractedMemberId shouldBe member.memberId.toString() | ||
} | ||
|
||
then("파싱된 권한(role)은 원래 멤버의 역할과 같아야 한다") { | ||
extractedAuthorities.size shouldBe 1 | ||
extractedAuthorities.first().authority shouldBe member.role?.name | ||
} | ||
} | ||
} | ||
|
||
|