Skip to content
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

[FEAT] JWT 기반 소셜 로그인 기능 구현 #15

Merged
merged 20 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c70064e
⚡️: fix Swagger, ApiResponseDto 패키지 수정 #14
orijoon98 Feb 16, 2024
5a71449
⚡️: fix PluApiApplication 패키지 수정 #14
orijoon98 Feb 16, 2024
ac90cd2
⚡️: fix Jpa, Querydsl 설정 수정 #14
orijoon98 Feb 16, 2024
0e9002a
🔥: remove 중복 ControllerAdvice 제거 #14
orijoon98 Feb 16, 2024
6c5f558
✨: feat JWT 관련 기능 추가 #14
orijoon98 Feb 16, 2024
ec7e202
✨: feat JWT 토큰 생성 서비스 추가 #14
orijoon98 Feb 16, 2024
342d8dd
✨: feat 카카오 API 연동 #14
orijoon98 Feb 16, 2024
aba0d2f
✨: feat 카카오 회원가입 API 추가 #14
orijoon98 Feb 16, 2024
65e6286
✨: feat 카카오 로그인 API 추가 #14
orijoon98 Feb 16, 2024
945b92f
✨: feat 토큰 갱신 API 추가 #14
orijoon98 Feb 16, 2024
1128c06
✨: feat 회원 인증용 interceptor, resolver 추가 #14
orijoon98 Feb 16, 2024
3d96f66
✨: feat 로그아웃 API 추가 #14
orijoon98 Feb 16, 2024
1fa63ad
✨: feat 애플 로그인 기능 추가 #14
orijoon98 Feb 16, 2024
64e3e8c
⚡️: fix Redis Template 의존성 문제 해결 #14
orijoon98 Feb 17, 2024
e7176bf
⚡️: fix QuerydslConfig 의존성 주입 방식 수정 #14
orijoon98 Mar 1, 2024
8663d1c
♻️: refactor RedisTemplate wrapping 적용, class 명 수정 #14
orijoon98 Mar 1, 2024
59832a3
♻️: refactor TokenService 패키지 수정 및 RedisHandler 사용하도록 수정 #14
orijoon98 Mar 1, 2024
1751374
♻️: refactor 토큰 생성 후 전달 방식 개선 #14
orijoon98 Mar 1, 2024
4b5fe80
♻️: refactor 예외처리 방식 수정 #14
orijoon98 Mar 1, 2024
28c6c84
⚡️: fix 불필요한 어노테이션 제거 #14
orijoon98 Mar 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ subprojects {
implementation("org.springframework.boot:spring-boot-starter-actuator")

// tools
implementation("org.springframework.boot:spring-boot-starter-validation")
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")

Expand All @@ -55,6 +54,4 @@ subprojects {
useJUnitPlatform()
}
}
//
//version = "0.0.1-SNAPSHOT"

38 changes: 25 additions & 13 deletions plu-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
plugins {
kotlin("jvm")
kotlin("jvm")
}
tasks.jar {
enabled = false
enabled = false
}

dependencies {
implementation(project(":plu-domain"))
implementation(project(":plu-external"))
implementation(project(":plu-common"))
implementation(project(":plu-domain"))
implementation(project(":plu-external"))
implementation(project(":plu-common"))

// web
implementation("org.springframework.boot:spring-boot-starter-web")
// web
implementation("org.springframework.boot:spring-boot-starter-web")

// Redis
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("org.springframework.session:spring-session-data-redis")
implementation(kotlin("stdlib-jdk8"))
// validation
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("jakarta.validation:jakarta.validation-api:3.0.2")

// swagger
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0")

// Redis
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("org.springframework.session:spring-session-data-redis")
implementation(kotlin("stdlib-jdk8"))

//jwt
implementation("io.jsonwebtoken:jjwt-api:0.11.2")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.2")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.2")
}
repositories {
mavenCentral()
mavenCentral()
}
kotlin {
jvmToolchain(17)
jvmToolchain(17)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.th.plu
package com.th.plu.api

import com.th.plu.common.PluCommonRoot
import com.th.plu.domain.PluDomainRoot
Expand Down
24 changes: 24 additions & 0 deletions plu-api/src/main/kotlin/com/th/plu/api/config/WebConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.th.plu.api.config

import com.th.plu.api.config.interceptor.AuthInterceptor
import com.th.plu.api.config.resolver.MemberIdResolver
import org.springframework.context.annotation.Configuration
import org.springframework.web.method.support.HandlerMethodArgumentResolver
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration
class WebConfig(
private val authInterceptor: AuthInterceptor,
private val memberIdResolver: MemberIdResolver
) : WebMvcConfigurer {

override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(authInterceptor)
}

override fun addArgumentResolvers(resolvers: MutableList<HandlerMethodArgumentResolver>) {
resolvers.add(memberIdResolver)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.th.plu.api.config.interceptor

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class Auth
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.th.plu.api.config.interceptor

import com.th.plu.common.constant.JwtKey
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.stereotype.Component
import org.springframework.web.method.HandlerMethod
import org.springframework.web.servlet.HandlerInterceptor

@Component
class AuthInterceptor(
private val loginCheckHandler: LoginCheckHandler
) : HandlerInterceptor {

override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
if (handler !is HandlerMethod) {
return true
}
handler.getMethodAnnotation(Auth::class.java) ?: return true
val memberId = loginCheckHandler.getMemberId(request)
request.setAttribute(JwtKey.MEMBER_ID, memberId)
return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.th.plu.api.config.interceptor

import com.th.plu.api.service.auth.jwt.JwtHandler
import com.th.plu.common.exception.code.ErrorCode
import com.th.plu.common.exception.model.UnauthorizedException
import jakarta.servlet.http.HttpServletRequest
import org.springframework.stereotype.Component

@Component
class LoginCheckHandler(
private val jwtHandler: JwtHandler
) {
fun getMemberId(request: HttpServletRequest): Long {
val bearerToken: String? = request.getHeader("Authorization")
if (!bearerToken.isNullOrBlank() && bearerToken.startsWith("Bearer ")) {
val accessToken = bearerToken.substring("Bearer ".length)
if (jwtHandler.validateToken(accessToken)) {
return jwtHandler.getMemberIdFromJwt(accessToken)
}
}
throw UnauthorizedException(ErrorCode.UNAUTHORIZED_EXCEPTION, "잘못된 JWT $bearerToken 입니다.")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.th.plu.api.config.resolver

@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class MemberId
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.th.plu.api.config.resolver

import com.th.plu.api.config.interceptor.Auth
import com.th.plu.common.constant.JwtKey
import com.th.plu.common.exception.code.ErrorCode
import com.th.plu.common.exception.model.InternalServerException
import org.springframework.core.MethodParameter
import org.springframework.stereotype.Component
import org.springframework.web.bind.support.WebDataBinderFactory
import org.springframework.web.context.request.NativeWebRequest
import org.springframework.web.method.support.HandlerMethodArgumentResolver
import org.springframework.web.method.support.ModelAndViewContainer

@Component
class MemberIdResolver : HandlerMethodArgumentResolver {

override fun supportsParameter(parameter: MethodParameter): Boolean {
return parameter.hasParameterAnnotation(MemberId::class.java) && Long::class.java == parameter.parameterType
}

override fun resolveArgument(
parameter: MethodParameter, mavContainer: ModelAndViewContainer?,
webRequest: NativeWebRequest, binderFactory: WebDataBinderFactory?
): Any? {
parameter.getMethodAnnotation(Auth::class.java)
?: throw InternalServerException(
ErrorCode.INTERNAL_SERVER_EXCEPTION,
"인증이 필요한 컨트롤러 입니다. @Auth 어노테이션을 붙여주세요."
)

return webRequest.getAttribute(JwtKey.MEMBER_ID, 0)
?: throw InternalServerException(
ErrorCode.INTERNAL_SERVER_EXCEPTION,
"MEMBER_ID 를 가져오지 못했습니다. ($parameter.javaClass - $parameter.method)"
)
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.th.plu.api.config.swagger

import com.th.plu.api.config.resolver.MemberId
import io.swagger.v3.oas.models.Components
import io.swagger.v3.oas.models.OpenAPI
import io.swagger.v3.oas.models.info.Info
import io.swagger.v3.oas.models.security.SecurityRequirement
import io.swagger.v3.oas.models.security.SecurityScheme
import org.springdoc.core.utils.SpringDocUtils
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

Expand All @@ -19,19 +21,22 @@ class SwaggerConfig {
@Bean
fun openAPI(): OpenAPI {
val info = Info()
.title(TITLE)
.description(DESCRIPTION)
.version(VERSION)
.title(TITLE)
.description(DESCRIPTION)
.version(VERSION)

val securityScheme = SecurityScheme()
.type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")
.`in`(SecurityScheme.In.HEADER).name("Authorization")
.type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")
.`in`(SecurityScheme.In.HEADER).name("Authorization")
val securityRequirement = SecurityRequirement().addList("Bearer Token")

return OpenAPI()
.components(Components().addSecuritySchemes("Bearer Token", securityScheme))
.security(listOf(securityRequirement))
.info(info)
.components(Components().addSecuritySchemes("Bearer Token", securityScheme))
.security(listOf(securityRequirement))
.info(info)
}

init {
SpringDocUtils.getConfig().addAnnotationsToIgnore(MemberId::class.java)
}
}

This file was deleted.

Loading
Loading