-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: BaseEntity 작성 global/entity 폴더에 created_at, updated_at 필드를 넣는 BaseEntity 클래스를 작성하였습니다. * feat: User Entity 생성 (#2) DB에 매핑될 User 객체를 생성하였습니다. Kakao, Apple 두 개의 소셜 타입을 구분할 SocialType 필드를 enum Type으로 추가하였습니다. * chore: Swagger, Logging 클래스 global 폴더로 이동 * feat: OAuthController 생성 및 콜백 메서드 생성 1. 클라이언트가 AccessToken을 전달할 때 접근하는 'POST /auth' 콜백 주소를 연결할 메서드를 생성하였습니다. 2. 컨트롤러 메서드의 request, response dto를 record 클래스로 정의하였습니다. * feat: login 로직을 구현한 OAuthService 생성 1. 팩토리 메서드 패턴을 적용하여 OAuthClient 인터페이스를 provider에 따라 구현체를 매핑하도록 설정하였습니다. 2. OAuthClient에서 소셜 사용자의 정보를 불러오고 User 객체로 변환하였습니다. 3. User가 기 가입한 회원이라면 바로 반환, 그렇지 않다면 DB에 등록 후 반환하기 위해 UserService에서 로직을 구현하였습니다. 4. 마지막으로 회원에 대한 JWT 를 발급하여 반환하였습니다. * build: syntax error 수정 * build: syntax error 수정 * build: Junit Test 관련 권한 추가 * feat: WebClient를 이용해 카카오 인증서버에서 사용자 추출 (#2) KakaoClient 구현체에서 WebClient 라이브러리를 사용해 kapi.kakao.com 서버에 접근하여 사용자 정보 (KakaoUserInfo)를 추출하는 코드입니다. * feat: JwtProvider 클래스 생성 1. createToken 메서드 2. validateToken 메서드 * chore: .gitignore 수정 * feat: 애플 로그인 구현 * refactor: OAuthHandler에서 Provider별 클래스 분리 구현 * test: OAuthServiceTest 로그인 성공 테스트 생성(#2) * feat: KakaoClient BASE_URL과 URI 분리 * style: dto 폴더구조 request, response 나눠서 변경 * refactor: SocialId만 이용해서 사용자 조회하도록 변경 * refactor: BaseEntity에 자동으로 날짜를 주입하도록 설정 --------- Co-authored-by: MingyeomKim <[email protected]>
- Loading branch information
1 parent
ef5159a
commit f1d9af7
Showing
25 changed files
with
461 additions
and
18 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,3 +35,4 @@ out/ | |
|
||
### VS Code ### | ||
.vscode/ | ||
src/main/resources/application.yml |
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
34 changes: 34 additions & 0 deletions
34
src/main/java/com/backend/auth/application/OAuthService.java
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,34 @@ | ||
package com.backend.auth.application; | ||
|
||
import com.backend.auth.application.client.OAuthHandler; | ||
import com.backend.auth.application.dto.response.OAuthUserInfo; | ||
import com.backend.auth.presentation.dto.request.LoginRequest; | ||
import com.backend.auth.presentation.dto.response.LoginResponse; | ||
import com.backend.global.util.JwtUtil; | ||
import com.backend.user.application.UserService; | ||
import com.backend.user.domain.User; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Service; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class OAuthService { | ||
|
||
@Value("${jwt.secret}") | ||
private String key; | ||
|
||
private Long expireTime = 1000 * 60 * 60L; | ||
|
||
private final UserService userService; | ||
private final OAuthHandler oAuthHandler; | ||
|
||
public LoginResponse login(LoginRequest loginRequest) throws Exception { | ||
OAuthUserInfo userInfo = oAuthHandler.getUserInfo(loginRequest.accessToken(), loginRequest.provider()); | ||
|
||
User uncheckedUser = User.from(userInfo, loginRequest.provider()); | ||
User user = userService.findUserOrRegister(uncheckedUser); | ||
|
||
return new LoginResponse(JwtUtil.generateToken(user, key, expireTime), user.getNickname()); | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
src/main/java/com/backend/auth/application/client/AppleClient.java
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,43 @@ | ||
package com.backend.auth.application.client; | ||
|
||
import com.backend.auth.application.dto.response.OAuthUserInfo; | ||
import com.backend.user.domain.SocialType; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpStatusCode; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.web.reactive.function.client.WebClient; | ||
import reactor.core.publisher.Mono; | ||
|
||
public class AppleClient implements OAuthClient { | ||
|
||
private static final String APPLE_API_URL = "https://appleid.apple.com/auth/token"; | ||
private final WebClient appleOauthLoginClient; | ||
|
||
public AppleClient (final WebClient webClient){ | ||
this.appleOauthLoginClient = appleOauthLoginClient(webClient); | ||
} | ||
|
||
@Override | ||
public boolean supports(SocialType provider) { | ||
return provider.isSameAs(SocialType.APPLE); | ||
} | ||
|
||
@Override | ||
public OAuthUserInfo getUserInfo(String accessToken) { | ||
return appleOauthLoginClient.get() | ||
.uri(APPLE_API_URL) | ||
.headers(h -> h.setBearerAuth(accessToken)) | ||
.retrieve() | ||
.onStatus(HttpStatusCode::is4xxClientError, response -> Mono.error(new Exception("Apple Login: 잘못된 토큰 정보입니다."))) | ||
.onStatus(HttpStatusCode::is5xxServerError, response -> Mono.error(new Exception("Apple Login: 내부 서버 오류"))) | ||
.bodyToMono(OAuthUserInfo.class) | ||
.block(); | ||
} | ||
|
||
private WebClient appleOauthLoginClient(WebClient webClient) { | ||
return webClient.mutate() | ||
.baseUrl(APPLE_API_URL) | ||
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) | ||
.build(); | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
src/main/java/com/backend/auth/application/client/KakaoClient.java
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,43 @@ | ||
package com.backend.auth.application.client; | ||
|
||
import com.backend.auth.application.dto.response.OAuthUserInfo; | ||
import com.backend.user.domain.SocialType; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpStatusCode; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.web.reactive.function.client.WebClient; | ||
import reactor.core.publisher.Mono; | ||
|
||
public class KakaoClient implements OAuthClient { | ||
private static final String KAKAO_BASE_URL = "https://kapi.kakao.com"; | ||
private static final String KAKAO_URI = "/v2/user/me"; | ||
private final WebClient kakaoOauthLoginClient; | ||
|
||
public KakaoClient(WebClient webClient){ | ||
this.kakaoOauthLoginClient = kakaoOauthLoginClient(webClient); | ||
} | ||
|
||
@Override | ||
public boolean supports(SocialType provider) { | ||
return provider.isSameAs(SocialType.KAKAO); | ||
} | ||
|
||
@Override | ||
public OAuthUserInfo getUserInfo(String accessToken) { | ||
return kakaoOauthLoginClient.get() | ||
.uri(KAKAO_URI) | ||
.headers(h -> h.setBearerAuth(accessToken)) | ||
.retrieve() | ||
.onStatus(HttpStatusCode::is4xxClientError, response -> Mono.error(new Exception("Kakao Login: 잘못된 토큰 정보입니다."))) | ||
.onStatus(HttpStatusCode::is5xxServerError, response -> Mono.error(new Exception("Kakao Login: 내부 서버 오류"))) | ||
.bodyToMono(OAuthUserInfo.class) | ||
.block(); | ||
} | ||
|
||
private WebClient kakaoOauthLoginClient(WebClient webClient) { | ||
return webClient.mutate() | ||
.baseUrl(KAKAO_BASE_URL) | ||
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) | ||
.build(); | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/com/backend/auth/application/client/OAuthClient.java
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,9 @@ | ||
package com.backend.auth.application.client; | ||
|
||
import com.backend.auth.application.dto.response.OAuthUserInfo; | ||
import com.backend.user.domain.SocialType; | ||
|
||
public interface OAuthClient { | ||
boolean supports(SocialType provider); | ||
OAuthUserInfo getUserInfo(String accessToken); | ||
} |
31 changes: 31 additions & 0 deletions
31
src/main/java/com/backend/auth/application/client/OAuthHandler.java
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,31 @@ | ||
package com.backend.auth.application.client; | ||
|
||
import com.backend.auth.application.dto.response.OAuthUserInfo; | ||
import com.backend.user.domain.SocialType; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.List; | ||
|
||
@Slf4j | ||
@Component | ||
public class OAuthHandler { | ||
private final List<OAuthClient> oAuthClientList; | ||
|
||
public OAuthHandler(List<OAuthClient> oAuthClientsList){ | ||
this.oAuthClientList = oAuthClientsList; | ||
} | ||
|
||
public OAuthUserInfo getUserInfo(String accessToken, String provider) throws Exception { | ||
OAuthClient oAuthClient = getClient(provider); | ||
return oAuthClient.getUserInfo(accessToken); | ||
} | ||
|
||
private OAuthClient getClient(String provider) throws Exception { | ||
SocialType socialType = SocialType.valueOf(provider); | ||
return oAuthClientList.stream() | ||
.filter(c -> c.supports(socialType)) | ||
.findFirst() | ||
.orElseThrow(Exception::new); // 커스텀 예외처리 "UnsupportedProviderException" 추가 | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
src/main/java/com/backend/auth/application/config/WebClientConfig.java
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,26 @@ | ||
package com.backend.auth.application.config; | ||
|
||
import com.backend.auth.application.client.AppleClient; | ||
import com.backend.auth.application.client.KakaoClient; | ||
import com.backend.auth.application.client.OAuthClient; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.web.reactive.function.client.WebClient; | ||
|
||
@Configuration | ||
public class WebClientConfig { | ||
@Bean | ||
public WebClient webClient(){ | ||
return WebClient.create(); | ||
} | ||
|
||
@Bean | ||
public OAuthClient kakaoClient(WebClient webClient){ | ||
return new KakaoClient(webClient); | ||
} | ||
|
||
@Bean | ||
public OAuthClient appleClient(WebClient webClient){ | ||
return new AppleClient(webClient); | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
src/main/java/com/backend/auth/application/dto/response/OAuthUserInfo.java
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,6 @@ | ||
package com.backend.auth.application.dto.response; | ||
|
||
public record OAuthUserInfo( | ||
String id, | ||
String nickname | ||
){ } |
32 changes: 32 additions & 0 deletions
32
src/main/java/com/backend/auth/presentation/OAuthController.java
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,32 @@ | ||
package com.backend.auth.presentation; | ||
|
||
import com.backend.auth.application.OAuthService; | ||
import com.backend.auth.presentation.dto.request.LoginRequest; | ||
import com.backend.auth.presentation.dto.response.LoginResponse; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
@Tag(name = "회원 인증", description = "소셜 로그인 API입니다.") | ||
@RequiredArgsConstructor | ||
@RestController | ||
public class OAuthController { | ||
|
||
private final OAuthService oauthService; | ||
|
||
@Operation(summary = "소셜 로그인", description = "소셜 로그인 후 사용자 토큰 발급") | ||
@ApiResponses({ | ||
@ApiResponse(responseCode = "200", description = "소셜 로그인 성공"), | ||
@ApiResponse(responseCode = "400", description = "잘못된 요청으로 인한 실패"), | ||
@ApiResponse(responseCode = "401", description = "접근 권한 없음") | ||
}) | ||
@PostMapping("/auth") | ||
public ResponseEntity<LoginResponse> generateAccessToken(@RequestBody LoginRequest loginRequest) throws Exception { | ||
return ResponseEntity.ok(oauthService.login(loginRequest)); | ||
} | ||
|
||
} |
6 changes: 6 additions & 0 deletions
6
src/main/java/com/backend/auth/presentation/dto/request/LoginRequest.java
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,6 @@ | ||
package com.backend.auth.presentation.dto.request; | ||
|
||
public record LoginRequest( | ||
String accessToken, | ||
String provider | ||
) { } |
7 changes: 7 additions & 0 deletions
7
src/main/java/com/backend/auth/presentation/dto/response/LoginResponse.java
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,7 @@ | ||
package com.backend.auth.presentation.dto.response; | ||
|
||
public record LoginResponse ( | ||
String accessToken, | ||
String nickname | ||
){} | ||
|
2 changes: 1 addition & 1 deletion
2
...va/com/backend/api/LoggingController.java → ...backend/global/api/LoggingController.java
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
2 changes: 1 addition & 1 deletion
2
...ava/com/backend/config/SwaggerConfig.java → .../backend/global/config/SwaggerConfig.java
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.backend.global.entity; | ||
|
||
import jakarta.persistence.Column; | ||
import jakarta.persistence.EntityListeners; | ||
import jakarta.persistence.MappedSuperclass; | ||
import org.mariadb.jdbc.plugin.codec.LocalDateTimeCodec; | ||
import org.springframework.data.annotation.CreatedDate; | ||
import org.springframework.data.annotation.LastModifiedDate; | ||
import org.springframework.data.jpa.domain.support.AuditingEntityListener; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
@MappedSuperclass | ||
@EntityListeners(AuditingEntityListener.class) | ||
public abstract class BaseEntity { | ||
@CreatedDate | ||
@Column(name="created_at", nullable=false, updatable=false) | ||
private LocalDateTime createdAt; | ||
|
||
@LastModifiedDate | ||
@Column(name="updated_at", nullable=false) | ||
private LocalDateTime updatedAt ; | ||
} |
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,24 @@ | ||
package com.backend.global.util; | ||
|
||
import com.backend.user.domain.User; | ||
import io.jsonwebtoken.*; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.Date; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class JwtUtil { | ||
|
||
public static String generateToken(User user, String key, Long expireTime) { | ||
Claims claims = Jwts.claims(); | ||
claims.put("userId", user.getId()); | ||
return Jwts.builder() | ||
.setClaims(claims) | ||
.setIssuedAt(new Date(System.currentTimeMillis())) | ||
.setExpiration(new Date(System.currentTimeMillis() + expireTime)) | ||
.signWith(SignatureAlgorithm.HS256, key) | ||
.compact(); | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
src/main/java/com/backend/user/application/UserService.java
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,20 @@ | ||
package com.backend.user.application; | ||
|
||
import com.backend.user.domain.User; | ||
import com.backend.user.domain.UserRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.util.Optional; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class UserService { | ||
|
||
private final UserRepository userRepository; | ||
|
||
public User findUserOrRegister(User uncheckedUser) { | ||
Optional<User> user = userRepository.findBySocialId(uncheckedUser.getSocialId()); | ||
return user.orElseGet(() -> userRepository.save(uncheckedUser)); | ||
} | ||
} |
Oops, something went wrong.