Skip to content

Commit

Permalink
refactor: 🔧 Migrating the Socket Module from Java to Kotlin (#211)
Browse files Browse the repository at this point in the history
* rename: application file java to kotlin

* chore: convert language from java to kotlin in the socket module

* fix: change application file to kotlin style

* fix: move @post-construct method function into the class

* feat: add kotlin log util

* refactor: convert auth_service from java to kotlin

* refactor: convert chat_message_send_service from java to kotlin

* refactor: convert stomp-message-util from java to kotlin

* refactor: convert last-message-id-save-service from java to kotlin

* refactor: convert status-service from java to kotlin

* refactor: convert chat-message-relay-service from java to kotlin

* refactor: convert auth-controller from java to kotlin

* refactor: convert chat-message-controller from java to kotlin

* refactor: convert status-controller from java to kotlin

* refactor: convert pre-authorized-spel-parser & aspect from java to kotlin

* refactor: convert user-principal from java to kotlin

* fix: principal.expired cannot use(lombok compile time), so convert domain method

* fix: add jvm-static annotation at create-method in the stomp-message-util

* rename: add evaluate prameter name

* fix: modify is_authenticated to static method

* chore: modify kotlin compile option cause method parameter data lost

* fix: divide evaluate logic from execution logic
  • Loading branch information
psychology50 authored Dec 26, 2024
1 parent 09336ff commit 30524c5
Show file tree
Hide file tree
Showing 29 changed files with 640 additions and 654 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
buildscript {
repositories {
mavenCentral()
gradlePluginPortal()
}
}

Expand Down Expand Up @@ -30,6 +31,7 @@ subprojects {

repositories {
mavenCentral()
gradlePluginPortal()
}

configurations {
Expand Down
12 changes: 12 additions & 0 deletions pennyway-socket/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
id 'java'
id 'org.jetbrains.kotlin.jvm' version '1.8.21'
id 'org.jetbrains.kotlin.plugin.spring' version '1.8.21'
}

bootJar { enabled = true }
Expand Down Expand Up @@ -32,4 +36,12 @@ dependencies {
implementation group: 'org.openapitools', name: 'jackson-databind-nullable', version: '0.2.6'

implementation 'org.springframework.boot:spring-boot-starter-validation:3.2.3'
}

tasks.withType(KotlinCompile) {
kotlinOptions {
freeCompilerArgs = ['-Xjsr305=strict']
javaParameters = true
jvmTarget = '17'
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package kr.co.pennyway;

import jakarta.annotation.PostConstruct
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import java.util.*

@SpringBootApplication
class PennywaySocketApplication {
@PostConstruct
fun setDefaultTimeZone() {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"))
}
}

fun main(args: Array<String>) {
runApplication<PennywaySocketApplication>(*args)
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package kr.co.pennyway.socket.common.aop;

import kr.co.pennyway.socket.common.annotation.PreAuthorize
import kr.co.pennyway.socket.common.exception.PreAuthorizeErrorCode
import kr.co.pennyway.socket.common.exception.PreAuthorizeErrorException
import kr.co.pennyway.socket.common.util.PreAuthorizeSpELParser
import kr.co.pennyway.socket.common.util.logger
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.reflect.MethodSignature
import org.springframework.context.ApplicationContext
import org.springframework.stereotype.Component
import java.lang.reflect.Method
import java.security.Principal

@Aspect
@Component
class PreAuthorizeAspect(private val applicationContext: ApplicationContext) {
private val log = logger()

/**
* {@link PreAuthorize} 어노테이션이 붙은 메서드를 가로채고 인증/인가를 수행합니다.
*
* @param joinPoint 가로챈 메서드의 실행 지점
* @return 인증/인가가 성공하면 원래 메서드의 실행 결과, 실패하면 UnauthorizedResponse
* @throws Throwable 메서드 실행 중 발생한 예외
*/
@Around("@annotation(kr.co.pennyway.socket.common.annotation.PreAuthorize)")
fun execute(joinPoint: ProceedingJoinPoint): Any = with(joinPoint) {
(signature as? MethodSignature)
?.method
?.let { method -> validateAccess(method, this) }
?: throw IllegalStateException("PreAuthorize는 메서드에만 적용할 수 있습니다")
}

private fun validateAccess(method: Method, joinPoint: ProceedingJoinPoint): Any {
val preAuthorize = method.requireAnnotation<PreAuthorize>()
val principal = joinPoint.args.findPrincipal()

evaluateAccess(
principal = principal,
preAuthorize = preAuthorize,
method = method,
args = joinPoint.args
)

return joinPoint.proceed()
}

private fun evaluateAccess(
principal: Principal?,
preAuthorize: PreAuthorize,
method: Method,
args: Array<Any>
) = PreAuthorizeSpELParser
.evaluate(
expression = preAuthorize.value,
method = method,
args = args,
applicationContext = applicationContext
)
.also { result -> handleEvaluationResult(result, principal) }

private fun handleEvaluationResult(
result: PreAuthorizeSpELParser.EvaluationResult,
principal: Principal?
) = when (result) {
is PreAuthorizeSpELParser.EvaluationResult.Permitted -> Unit
is PreAuthorizeSpELParser.EvaluationResult.Denied.Unauthenticated -> {
log.warn("인증 실패: {}", principal)
throw PreAuthorizeErrorException(PreAuthorizeErrorCode.UNAUTHENTICATED)
}

is PreAuthorizeSpELParser.EvaluationResult.Denied.Unauthorized -> {
log.warn("인가 실패: {}", principal)
throw PreAuthorizeErrorException(PreAuthorizeErrorCode.FORBIDDEN)
}
}

private companion object {
inline fun <reified T : Annotation> Method.requireAnnotation(): T =
getAnnotation(T::class.java)
?: throw IllegalStateException("Required annotation ${T::class.simpleName} not found")

fun Array<Any>.findPrincipal(): Principal? = asSequence()
.filterIsInstance<Principal>()
.firstOrNull()
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package kr.co.pennyway.socket.common.security.authenticate;

import kr.co.pennyway.domain.domains.user.domain.User
import kr.co.pennyway.domain.domains.user.type.Role
import java.security.Principal
import java.time.LocalDateTime

data class UserPrincipal(
val userId: Long,
private var _name: String,
var username: String,
var role: Role,
var isChatNotify: Boolean,
var expiresAt: LocalDateTime,
var deviceId: String,
var deviceName: String
) : Principal {
fun isAuthenticated(): Boolean = !isExpired()

fun updateExpiresAt(newExpiresAt: LocalDateTime) {
this.expiresAt = newExpiresAt
}

override fun getName(): String = userId.toString()

fun getDefaultName(): String = _name

private fun isExpired(): Boolean = LocalDateTime.now().isAfter(expiresAt)

companion object {
@JvmStatic
fun of(
user: User,
expiresAt: LocalDateTime,
deviceId: String,
deviceName: String
): UserPrincipal = UserPrincipal(
userId = user.id,
_name = user.name,
username = user.username,
role = user.role,
isChatNotify = user.notifySetting.isChatNotify,
expiresAt = expiresAt,
deviceId = deviceId,
deviceName = deviceName
)
}
}
Loading

0 comments on commit 30524c5

Please sign in to comment.