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

Feature/oauth logic #30

Merged
merged 9 commits into from
Oct 12, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ import org.springframework.data.jpa.repository.JpaRepository
interface ClientRepository: JpaRepository<Client, Long> {
fun findAllByCreatedBy(createdBy: User): List<Client>
fun findByIdAndCreatedBy(clientId: Long, createdBy: User): Client?
fun findByClientId(clientId: String): Client?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.msg.gauth.domain.oauth.exception

import com.msg.gauth.global.exception.ErrorCode
import com.msg.gauth.global.exception.exceptions.BasicException

class ClientSecretMismatchException: BasicException(ErrorCode.CLIENT_SECRET_MISMATCH)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.msg.gauth.domain.oauth.exception

import com.msg.gauth.global.exception.ErrorCode
import com.msg.gauth.global.exception.exceptions.BasicException

class OauthCodeExpiredException: BasicException(ErrorCode.OAUTH_CODE_EXPIRED)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.msg.gauth.domain.oauth.exception

import com.msg.gauth.global.exception.ErrorCode
import com.msg.gauth.global.exception.exceptions.BasicException

class UserStatePendingException: BasicException(ErrorCode.USER_STATE_PENDING)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.msg.gauth.domain.oauth.presentation

import com.msg.gauth.domain.oauth.presentation.dto.request.OauthLoginRequestDto
import com.msg.gauth.domain.oauth.presentation.dto.request.UserInfoRequestDto
import com.msg.gauth.domain.oauth.presentation.dto.response.OauthLoginResponseDto
import com.msg.gauth.domain.oauth.presentation.dto.response.UserInfoResponseDto
import com.msg.gauth.domain.oauth.services.OauthLoginService
import com.msg.gauth.domain.oauth.services.OauthUserInfoService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import javax.validation.Valid

@RestController
@RequestMapping("/oauth")
class OauthController(
val oauthLoginService: OauthLoginService,
val oauthUserInfoService: OauthUserInfoService,
){
@PostMapping("/login")
fun oauthLogin(@Valid @RequestBody oauthLoginRequestDto : OauthLoginRequestDto): ResponseEntity<OauthLoginResponseDto> =
ResponseEntity.ok(oauthLoginService.execute(oauthLoginRequestDto))

@PostMapping("/user")
fun getUserInfo(@RequestBody userInfoRequestDto: UserInfoRequestDto): ResponseEntity<UserInfoResponseDto> =
ResponseEntity.ok(oauthUserInfoService.execute(userInfoRequestDto))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.msg.gauth.domain.oauth.presentation.dto.request

import javax.validation.constraints.NotBlank
import javax.validation.constraints.Pattern

data class OauthLoginRequestDto(
@field:NotBlank
@field:Pattern(regexp = "^[a-zA-Z0-9]+@gsm.hs.kr$")
val email: String,

@field:NotBlank
val password: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.msg.gauth.domain.oauth.presentation.dto.request

data class UserInfoRequestDto(
val code: String,
val clientId: String,
val clientSecret: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.msg.gauth.domain.oauth.presentation.dto.response

data class OauthLoginResponseDto(
val code: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.msg.gauth.domain.oauth.presentation.dto.response

import com.msg.gauth.domain.user.enums.Gender

data class UserInfoResponseDto(
val email: String,
val grade: Int?,
val classNum: Int?,
val num: Int?,
val gender: Gender?,
baekteun marked this conversation as resolved.
Show resolved Hide resolved
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.msg.gauth.domain.oauth.services

import com.msg.gauth.domain.auth.exception.PasswordMismatchException
import com.msg.gauth.domain.oauth.presentation.dto.request.OauthLoginRequestDto
import com.msg.gauth.domain.oauth.presentation.dto.response.OauthLoginResponseDto
import com.msg.gauth.domain.user.exception.UserNotFoundException
import com.msg.gauth.domain.user.repository.UserRepository
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.util.*

@Service
class OauthLoginService(
private val userRepository: UserRepository,
private val passwordEncoder: PasswordEncoder,
private val redisTemplate: RedisTemplate<String, String>,
){
@Transactional(readOnly = true)
fun execute(oauthLoginRequestDto: OauthLoginRequestDto): OauthLoginResponseDto {
val valueOperation = redisTemplate.opsForValue()
val user = userRepository.findByEmail(oauthLoginRequestDto.email) ?: throw UserNotFoundException()
if (passwordEncoder.matches(oauthLoginRequestDto.password, user.password)) {
throw PasswordMismatchException()
}
val code = UUID.randomUUID().toString().split(".")[0]
valueOperation.set(code, user.email, 1000L * 60 * 5)
return OauthLoginResponseDto(
code = code,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.msg.gauth.domain.oauth.services

import com.msg.gauth.domain.client.exception.ClientNotFindException
import com.msg.gauth.domain.client.repository.ClientRepository
import com.msg.gauth.domain.oauth.exception.ClientSecretMismatchException
import com.msg.gauth.domain.oauth.exception.OauthCodeExpiredException
import com.msg.gauth.domain.oauth.exception.UserStatePendingException
import com.msg.gauth.domain.oauth.presentation.dto.request.UserInfoRequestDto
import com.msg.gauth.domain.oauth.presentation.dto.response.UserInfoResponseDto
import com.msg.gauth.domain.user.enums.UserState
import com.msg.gauth.domain.user.exception.UserNotFoundException
import com.msg.gauth.domain.user.repository.UserRepository
import com.msg.gauth.global.exception.exceptions.BasicException
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class OauthUserInfoService(
private val redisTemplate: RedisTemplate<String, String>,
private val clientRepository: ClientRepository,
private val userRepository: UserRepository,
){
@Transactional(readOnly = true)
fun execute(userInfoRequestDto: UserInfoRequestDto): UserInfoResponseDto{
val valueOperation = redisTemplate.opsForValue()
val client = clientRepository.findByClientId(userInfoRequestDto.clientId) ?: throw ClientNotFindException()
if(client.clientSecret != userInfoRequestDto.clientSecret)
throw ClientSecretMismatchException()
val email = valueOperation.get(userInfoRequestDto.code) ?: throw OauthCodeExpiredException()
val user = userRepository.findByEmail(email) ?: throw UserNotFoundException()
if(user.state == UserState.PENDING)
throw UserStatePendingException()
return UserInfoResponseDto(
email = user.email,
grade = user.grade,
classNum = user.classNum,
num = user.num,
gender = user.gender
)
}
}
4 changes: 4 additions & 0 deletions src/main/kotlin/com/msg/gauth/global/exception/ErrorCode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ enum class ErrorCode(
) {
BAD_REQUEST("잘못된 요청", 400),
PASSWORD_MISMATCH("비밀번호가 일치하지 않습니다.", 400),
CLIENT_SECRET_MISMATCH("클라이언트 시크릿이 일치하지 않습니다.", 400),

AUTH_CODE_EXPIRED("메일 인증이 만료되었습니다.", 401),
UNAUTHORIZED("권한 없음", 401),
Expand All @@ -15,6 +16,9 @@ enum class ErrorCode(
INVALID_REFRESH_TOKEN("리프레시 토큰이 변질되었습니다", 401),
EMAIL_NOT_VERIFIED("인증된 이메일이 아닙니다.", 401),
USER_NOT_SAME("유저가 일치하지 않습니다", 401),
OAUTH_CODE_EXPIRED("oauth 코드가 만료되었습니다.", 401),

USER_STATE_PENDING("유저가 보류중 입니다.", 403),

NOT_FOUND("리소스를 찾을수 없음", 404),
USER_NOT_FOUND("해당 유저를 찾을 수 없습니다.", 404),
Expand Down