[#26] feat: controller 규격화된 response 적용
- 모든 controller에 ApplicationResponse 적용 - 인증 프로세스 내 발생 가능한 예외 BusinessException 처리 - 불필요한 클래스 정리
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
package io.beaniejoy.dongnecafe.common.security
|
||||
|
||||
import io.beaniejoy.dongnecafe.common.error.constant.ErrorCode
|
||||
import io.beaniejoy.dongnecafe.common.error.exception.BusinessException
|
||||
import io.beaniejoy.dongnecafe.security.SecurityUser
|
||||
import mu.KLogging
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.BadCredentialsException
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.userdetails.UserDetailsService
|
||||
@@ -29,7 +30,7 @@ class ApiAuthenticationProvider(
|
||||
|
||||
val user = userDetailsService.loadUserByUsername(email) as SecurityUser
|
||||
if (!passwordEncoder.matches(password, user.password)) {
|
||||
throw BadCredentialsException("Input password does not match stored password")
|
||||
throw BusinessException(ErrorCode.AUTH_PASSWORD_NOT_VALID)
|
||||
}
|
||||
|
||||
logger.info { "User password ${user.password}" }
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package io.beaniejoy.dongnecafe.common.security
|
||||
|
||||
import io.beaniejoy.dongnecafe.common.error.constant.ErrorCode
|
||||
import io.beaniejoy.dongnecafe.common.error.exception.BusinessException
|
||||
import io.beaniejoy.dongnecafe.domain.member.entity.Member
|
||||
import io.beaniejoy.dongnecafe.domain.member.repository.MemberRepository
|
||||
import io.beaniejoy.dongnecafe.error.MemberDeactivatedException
|
||||
import io.beaniejoy.dongnecafe.security.SecurityUser
|
||||
import mu.KLogging
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||
import org.springframework.security.core.userdetails.UserDetailsService
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
|
||||
@@ -22,12 +22,12 @@ class UserDetailsServiceImpl(
|
||||
return memberRepository.findByEmail(email)?.let {
|
||||
logger.info { "[LOAD MEMBER] email: ${it.email}, role: ${it.roleType}, activated: ${it.activated}" }
|
||||
createSecurityUser(it)
|
||||
} ?: throw UsernameNotFoundException("${email} is not found")
|
||||
} ?: throw BusinessException(ErrorCode.AUTH_MEMBER_NOT_FOUND)
|
||||
}
|
||||
|
||||
private fun createSecurityUser(member: Member): SecurityUser {
|
||||
if (member.activated.not()) {
|
||||
throw MemberDeactivatedException(member.email)
|
||||
throw BusinessException(ErrorCode.AUTH_MEMBER_DEACTIVATED)
|
||||
}
|
||||
|
||||
return SecurityUser(
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package io.beaniejoy.dongnecafe.common.security.model
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import org.springframework.security.core.GrantedAuthority
|
||||
|
||||
data class AuthenticationResult(
|
||||
val email: String,
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
val authorities: Collection<GrantedAuthority> = listOf(),
|
||||
val msg: String
|
||||
)
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.beaniejoy.dongnecafe.controller
|
||||
|
||||
import io.beaniejoy.dongnecafe.common.response.ApplicationResponse
|
||||
import io.beaniejoy.dongnecafe.security.JwtTokenUtils
|
||||
import io.beaniejoy.dongnecafe.domain.member.model.request.SignInRequest
|
||||
import io.beaniejoy.dongnecafe.model.TokenResponse
|
||||
@@ -16,7 +17,7 @@ class AuthController(
|
||||
private val jwtTokenUtils: JwtTokenUtils
|
||||
) {
|
||||
@PostMapping("/authenticate")
|
||||
fun signIn(@RequestBody signInRequest: SignInRequest): TokenResponse {
|
||||
fun signIn(@RequestBody signInRequest: SignInRequest): ApplicationResponse {
|
||||
val authentication = authService.signIn(
|
||||
email = signInRequest.email,
|
||||
password = signInRequest.password
|
||||
@@ -24,6 +25,8 @@ class AuthController(
|
||||
|
||||
val accessToken = jwtTokenUtils.createToken(authentication)
|
||||
|
||||
return TokenResponse(accessToken)
|
||||
return ApplicationResponse
|
||||
.success("success authenticate")
|
||||
.data(TokenResponse(accessToken))
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.beaniejoy.dongnecafe.controller
|
||||
|
||||
import io.beaniejoy.dongnecafe.common.response.ApplicationResponse
|
||||
import io.beaniejoy.dongnecafe.domain.member.model.request.MemberRegisterRequest
|
||||
import io.beaniejoy.dongnecafe.service.MemberService
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
@@ -13,7 +14,11 @@ class MemberController(
|
||||
private val memberService: MemberService
|
||||
) {
|
||||
@PostMapping("/sign-up")
|
||||
fun signUp(@RequestBody resource: MemberRegisterRequest): Long {
|
||||
return memberService.registerMember(resource)
|
||||
fun signUp(@RequestBody resource: MemberRegisterRequest): ApplicationResponse {
|
||||
val registerMemberId = memberService.registerMember(resource)
|
||||
|
||||
return ApplicationResponse
|
||||
.success("success sign up")
|
||||
.data(registerMemberId)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
package io.beaniejoy.dongnecafe.error
|
||||
|
||||
class MemberExistedException(email: String): RuntimeException("Member[$email] is already existed")
|
||||
@@ -1,3 +0,0 @@
|
||||
package io.beaniejoy.dongnecafe.error
|
||||
|
||||
class MemberDeactivatedException(email: String): RuntimeException("Member[$email] is deactivated")
|
||||
@@ -1,27 +0,0 @@
|
||||
package io.beaniejoy.dongnecafe.error.handler
|
||||
|
||||
import io.beaniejoy.dongnecafe.common.response.ApplicationResponse
|
||||
import mu.KLogging
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice
|
||||
|
||||
// TODO 통합된 에러 핸들링 필요(ErrorResponse 규격화)
|
||||
@RestControllerAdvice
|
||||
class CommonControllerAdvice {
|
||||
|
||||
companion object : KLogging()
|
||||
|
||||
@ExceptionHandler(AuthenticationException::class)
|
||||
fun handleAuthenticationException(e: AuthenticationException): ResponseEntity<ApplicationResponse<Any?>> {
|
||||
logger.error { "AuthenticationException: ${e.message}" }
|
||||
return ResponseEntity.ok().body(
|
||||
ApplicationResponse(
|
||||
code = HttpStatus.BAD_REQUEST.value(),
|
||||
message = "계정 혹은 비밀번호가 일치하지 않습니다."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
package io.beaniejoy.dongnecafe.service
|
||||
|
||||
import io.beaniejoy.dongnecafe.common.error.constant.ErrorCode
|
||||
import io.beaniejoy.dongnecafe.common.error.exception.BusinessException
|
||||
import io.beaniejoy.dongnecafe.domain.member.entity.Member
|
||||
import io.beaniejoy.dongnecafe.domain.member.model.request.MemberRegisterRequest
|
||||
import io.beaniejoy.dongnecafe.domain.member.repository.MemberRepository
|
||||
import io.beaniejoy.dongnecafe.error.MemberExistedException
|
||||
import org.springframework.security.crypto.password.PasswordEncoder
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
@@ -16,7 +17,7 @@ class MemberService(
|
||||
) {
|
||||
fun registerMember(resource: MemberRegisterRequest): Long {
|
||||
memberRepository.findByEmail(resource.email!!)?.also {
|
||||
throw MemberExistedException(resource.email!!)
|
||||
throw BusinessException(ErrorCode.MEMBER_EXISTED)
|
||||
}
|
||||
|
||||
val registeredMember = memberRepository.save(
|
||||
|
||||
@@ -11,13 +11,13 @@ import org.springframework.web.bind.annotation.RestControllerAdvice
|
||||
@RestControllerAdvice
|
||||
class BasicControllerAdvice {
|
||||
|
||||
companion object: KLogging()
|
||||
companion object : KLogging()
|
||||
|
||||
// 비즈니스 로직 상 에러 처리
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@ExceptionHandler(BusinessException::class)
|
||||
fun handleBusinessException(e: BusinessException): ApplicationResponse {
|
||||
logger.error { "[BusinessException] ${e.errorCode.name}" }
|
||||
|
||||
return ApplicationResponse.fail(e.errorCode, "error")
|
||||
logger.error { "[${BusinessException::class.simpleName}], <ErrorCode>: ${e.errorCode.name}, <ErrorMessage>: ${e.message}" }
|
||||
return ApplicationResponse.fail(errorCode = e.errorCode)
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package io.beaniejoy.dongnecafe.common.error.constant
|
||||
|
||||
enum class Domain {
|
||||
AUTH,
|
||||
MEMBER,
|
||||
CAFE,
|
||||
CAFE_MENU
|
||||
}
|
||||
@@ -1,13 +1,28 @@
|
||||
package io.beaniejoy.dongnecafe.common.error.constant
|
||||
|
||||
import io.beaniejoy.dongnecafe.common.error.constant.Domain.CAFE
|
||||
import io.beaniejoy.dongnecafe.common.error.constant.Domain.*
|
||||
import io.beaniejoy.dongnecafe.common.error.constant.SubCategory.*
|
||||
|
||||
enum class ErrorCode(
|
||||
val domain: Domain,
|
||||
val subCategory: SubCategory
|
||||
) {
|
||||
// AUTH(security 관련)
|
||||
AUTH_COMMON_EXCEPTION(AUTH, COMMON),
|
||||
AUTH_MEMBER_NOT_FOUND(AUTH, INVALID_AUTHENTICATE_REQUEST),
|
||||
AUTH_PASSWORD_NOT_VALID(AUTH, INVALID_AUTHENTICATE_REQUEST),
|
||||
AUTH_MEMBER_DEACTIVATED(AUTH, DEACTIVATED),
|
||||
|
||||
// MEMBER
|
||||
MEMBER_EXISTED(MEMBER, EXISTED),
|
||||
|
||||
// CAFE
|
||||
CAFE_NOT_FOUND(CAFE, NOT_FOUND),
|
||||
CAFE_EXISTED(CAFE, EXISTED);
|
||||
|
||||
companion object {
|
||||
fun convertOrNull(value: String?): ErrorCode? {
|
||||
return values().find { it.name === value }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package io.beaniejoy.dongnecafe.common.error.constant
|
||||
|
||||
enum class SubCategory {
|
||||
COMMON,
|
||||
INVALID_AUTHENTICATE_REQUEST,
|
||||
NOT_FOUND,
|
||||
EXISTED
|
||||
EXISTED,
|
||||
DEACTIVATED
|
||||
}
|
||||
@@ -30,11 +30,15 @@ class ApplicationResponse {
|
||||
|
||||
companion object {
|
||||
fun success(message: String? = null): ApplicationResponse {
|
||||
return ApplicationResponse(ResultCode.SUCCESS, message)
|
||||
return ApplicationResponse(resultCode = ResultCode.SUCCESS, message = message)
|
||||
}
|
||||
|
||||
fun fail(errorCode: ErrorCode, message: String?): ApplicationResponse {
|
||||
return ApplicationResponse(ResultCode.FAIL, errorCode, message)
|
||||
fun fail(errorCode: ErrorCode, message: String? = null): ApplicationResponse {
|
||||
return ApplicationResponse(
|
||||
resultCode = ResultCode.FAIL,
|
||||
errorCode = errorCode,
|
||||
message = message
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.security.servlet.PathRequest
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.http.HttpMethod
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer
|
||||
import org.springframework.security.config.http.SessionCreationPolicy
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
|
||||
@Configuration
|
||||
@@ -27,6 +27,10 @@ class SecurityConfig {
|
||||
.authorizeRequests()
|
||||
.anyRequest().permitAll()
|
||||
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 토큰 방식(세션 불필요)
|
||||
|
||||
.and()
|
||||
.also { jwtAuthenticationConfigurer(it) }
|
||||
.build()
|
||||
|
||||
@@ -3,10 +3,7 @@ package io.beaniejoy.dongnecafe.domain.cafe.controller
|
||||
import io.beaniejoy.dongnecafe.common.response.ApplicationResponse
|
||||
import io.beaniejoy.dongnecafe.domain.cafe.model.request.CafeRegisterRequest
|
||||
import io.beaniejoy.dongnecafe.domain.cafe.model.request.CafeUpdateRequest
|
||||
import io.beaniejoy.dongnecafe.domain.cafe.model.response.CafeDetailedInfo
|
||||
import io.beaniejoy.dongnecafe.domain.cafe.model.response.CafeSearchInfo
|
||||
import io.beaniejoy.dongnecafe.domain.cafe.service.CafeService
|
||||
import org.springframework.data.domain.Page
|
||||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.data.domain.Sort
|
||||
import org.springframework.data.web.PageableDefault
|
||||
@@ -30,7 +27,9 @@ class CafeController(
|
||||
cafeMenuRequestList = resource.cafeMenuList
|
||||
)
|
||||
|
||||
return ApplicationResponse.success("OK").data(newCafeId)
|
||||
return ApplicationResponse
|
||||
.success("OK")
|
||||
.data(newCafeId)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,15 +39,23 @@ class CafeController(
|
||||
fun searchCafeList(
|
||||
@PageableDefault(sort = ["name"], direction = Sort.Direction.ASC, page = 0, size = 10) pageable: Pageable
|
||||
): ApplicationResponse {
|
||||
return ApplicationResponse.success().data(cafeService.searchCafeList(pageable))
|
||||
val searchCafes = cafeService.searchCafeList(pageable)
|
||||
|
||||
return ApplicationResponse
|
||||
.success()
|
||||
.data(searchCafes)
|
||||
}
|
||||
|
||||
/**
|
||||
* 단일 카페 상세 조회
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
fun getDetailedInfo(@PathVariable("id") id: Long): CafeDetailedInfo {
|
||||
return cafeService.getDetailedInfoByCafeId(id)
|
||||
fun getDetailedInfo(@PathVariable("id") id: Long): ApplicationResponse {
|
||||
val cafeDetailedInfo = cafeService.getDetailedInfoByCafeId(id)
|
||||
|
||||
return ApplicationResponse
|
||||
.success()
|
||||
.data(cafeDetailedInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,7 +66,7 @@ class CafeController(
|
||||
fun updateInfo(
|
||||
@PathVariable("id") id: Long,
|
||||
@RequestBody resource: CafeUpdateRequest
|
||||
): String {
|
||||
): ApplicationResponse {
|
||||
cafeService.updateInfo(
|
||||
id = id,
|
||||
name = resource.name!!,
|
||||
@@ -68,6 +75,7 @@ class CafeController(
|
||||
description = resource.description!!
|
||||
)
|
||||
|
||||
return "Successfully Cafe[$id] Info Updated"
|
||||
return ApplicationResponse.success("Successfully Cafe[$id] Info Updated")
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,10 @@
|
||||
package io.beaniejoy.dongnecafe.domain.cafe.controller
|
||||
|
||||
import io.beaniejoy.dongnecafe.common.response.ApplicationResponse
|
||||
import io.beaniejoy.dongnecafe.domain.cafe.model.request.CafeMenuBulkDeleteRequest
|
||||
import io.beaniejoy.dongnecafe.domain.cafe.model.request.CafeMenuUpdateRequest
|
||||
import io.beaniejoy.dongnecafe.domain.cafe.model.response.CafeMenuDetailedInfo
|
||||
import io.beaniejoy.dongnecafe.domain.cafe.service.CafeMenuService
|
||||
import org.springframework.web.bind.annotation.DeleteMapping
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.PatchMapping
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import org.springframework.web.bind.annotation.*
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/cafes/{cafeId}/menus")
|
||||
@@ -24,11 +18,15 @@ class CafeMenuController(
|
||||
fun getDetailedInfo(
|
||||
@PathVariable("cafeId") cafeId: Long,
|
||||
@PathVariable("menuId") menuId: Long
|
||||
): CafeMenuDetailedInfo {
|
||||
return cafeMenuService.getDetailedInfoByMenuId(
|
||||
): ApplicationResponse {
|
||||
val cafeMenuDetailedInfo = cafeMenuService.getDetailedInfoByMenuId(
|
||||
menuId = menuId,
|
||||
cafeId = cafeId
|
||||
)
|
||||
|
||||
return ApplicationResponse
|
||||
.success()
|
||||
.data(cafeMenuDetailedInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,14 +40,14 @@ class CafeMenuController(
|
||||
@PathVariable("cafeId") cafeId: Long,
|
||||
@PathVariable("menuId") menuId: Long,
|
||||
@RequestBody cafeMenuUpdateRequest: CafeMenuUpdateRequest
|
||||
): String {
|
||||
): ApplicationResponse {
|
||||
cafeMenuService.updateInfoAndBulkUpdate(
|
||||
menuId = menuId,
|
||||
cafeId = cafeId,
|
||||
resource = cafeMenuUpdateRequest
|
||||
)
|
||||
|
||||
return "Success Update Cafe[$cafeId]'s CafeMenu[$menuId]"
|
||||
return ApplicationResponse.success("Success Update Cafe[$cafeId]'s CafeMenu[$menuId]")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,13 +57,13 @@ class CafeMenuController(
|
||||
fun delete(
|
||||
@PathVariable("cafeId") cafeId: Long,
|
||||
@PathVariable("menuId") menuId: Long
|
||||
): String {
|
||||
): ApplicationResponse {
|
||||
cafeMenuService.deleteByCafeMenuId(
|
||||
menuId = menuId,
|
||||
cafeId = cafeId
|
||||
)
|
||||
|
||||
return "Success Delete Cafe[$cafeId]'s CafeMenu[$menuId]"
|
||||
return ApplicationResponse.success("Success Delete Cafe[$cafeId]'s CafeMenu[$menuId]")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,9 +73,9 @@ class CafeMenuController(
|
||||
fun bulkDelete(
|
||||
@PathVariable("cafeId") cafeId: Long,
|
||||
@RequestBody resource: CafeMenuBulkDeleteRequest
|
||||
): String {
|
||||
): ApplicationResponse {
|
||||
cafeMenuService.bulkDelete(cafeId, resource.cafeMenuIdList)
|
||||
|
||||
return "Success Delete Cafe[$cafeId]'s CafeMenu List"
|
||||
return ApplicationResponse.success("Success Delete Cafe[$cafeId]'s CafeMenu List")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user