Skip to content

Commit

Permalink
merge(SignUp) : 회원가입 구현
Browse files Browse the repository at this point in the history
[✨feat] 회원가입 구현
  • Loading branch information
0-tae authored Feb 22, 2025
2 parents ff091d0 + b57db18 commit ce3e546
Show file tree
Hide file tree
Showing 72 changed files with 1,894 additions and 79 deletions.
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ ext {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.testng:testng:7.1.0'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
Expand All @@ -43,6 +44,11 @@ dependencies {
// Actuator
implementation 'org.springframework.boot:spring-boot-starter-actuator'

// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'

// QueryDSL
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}:jakarta"
annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}:jakarta"
Expand Down
64 changes: 64 additions & 0 deletions src/main/java/org/noostak/auth/api/OauthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.noostak.auth.api;


import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.noostak.auth.application.OauthService;
import org.noostak.auth.application.OauthServiceFactory;
import org.noostak.auth.application.jwt.JwtToken;
import org.noostak.auth.common.exception.AuthErrorCode;
import org.noostak.auth.common.exception.AuthException;
import org.noostak.auth.common.success.AuthSuccessCode;
import org.noostak.auth.domain.vo.AuthId;
import org.noostak.auth.dto.SignUpResponse;
import org.noostak.global.success.SuccessResponse;
import org.noostak.auth.application.AuthInfoService;
import org.noostak.auth.dto.SignUpRequest;
import org.noostak.member.application.MemberService;
import org.noostak.member.domain.Member;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/api/v1/auth")
public class OauthController {

private final OauthServiceFactory oauthServiceFactory;
private final AuthInfoService authInfoService;
private final MemberService memberService;

@PostMapping("/sign-up")
public ResponseEntity<?> signUp(HttpServletRequest request, @ModelAttribute SignUpRequest requestDto){
String code = request.getHeader("Authorization");

// authType 을 기준으로 OauthService 선택하기
String authType = requestDto.getAuthType().toUpperCase();
OauthService oauthService = oauthServiceFactory.getService(authType);

// code를 통해서 AccessToken 및 RefreshToken 가져오기
JwtToken jwtToken = oauthService.requestToken(code);
String accessToken = jwtToken.getAccessToken();
log.info("jwtToken : {}", jwtToken);

// 소셜 로그인 진행하기
AuthId authId = oauthService.login(accessToken);


// 동일 소셜 계정으로 가입이 되어있는지 확인하기
if(authInfoService.hasAuthInfo(authId)){
throw new AuthException(AuthErrorCode.AUTHID_ALREADY_EXISTS,authId.value());
}

// 멤버 생성하기
Member member = memberService.createMember(requestDto);

// 멤버와 연관된 AuthInfo 생성하기
SignUpResponse response =
authInfoService.createAuthInfo(authType, authId, jwtToken, member);

return ResponseEntity.ok((SuccessResponse.of(AuthSuccessCode.SIGNUP_COMPLETED,response)));
}
}
14 changes: 14 additions & 0 deletions src/main/java/org/noostak/auth/application/AuthInfoService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.noostak.auth.application;

import org.noostak.auth.application.jwt.JwtToken;
import org.noostak.auth.domain.vo.AuthId;
import org.noostak.auth.dto.SignUpResponse;
import org.noostak.member.domain.Member;

public interface AuthInfoService {
SignUpResponse createAuthInfo(String authType, AuthId authId, JwtToken jwtToken, Member member);

boolean hasAuthInfo(String authId);

boolean hasAuthInfo(AuthId authId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.noostak.auth.application;

import lombok.RequiredArgsConstructor;
import org.noostak.auth.application.jwt.JwtToken;
import org.noostak.auth.domain.AuthInfo;
import org.noostak.auth.domain.AuthInfoRepository;
import org.noostak.auth.domain.vo.AuthId;
import org.noostak.auth.domain.vo.AuthType;
import org.noostak.auth.domain.vo.RefreshToken;
import org.noostak.auth.dto.SignUpResponse;
import org.noostak.member.domain.Member;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


@Service
@RequiredArgsConstructor
public class AuthInfoServiceImpl implements AuthInfoService{

private final AuthInfoRepository authInfoRepository;
@Override
@Transactional
public SignUpResponse createAuthInfo(String authType, AuthId authId, JwtToken jwtToken, Member member) {
AuthInfo newAuthInfo = createAuthInfo(
AuthType.from(authType),
authId,
RefreshToken.from(jwtToken.getRefreshToken()),
member
);

saveAuthInfo(newAuthInfo);

return SignUpResponse.of(
jwtToken.getAccessToken(),
jwtToken.getRefreshToken(),
member.getId(),
authType
);
}

private AuthInfo createAuthInfo(AuthType authType, AuthId authId, RefreshToken refreshToken, Member member) {
return AuthInfo.of(
authType,
authId,
refreshToken,
member
);
}

private AuthInfo saveAuthInfo(AuthInfo authInfo){
return authInfoRepository.save(authInfo);
}

@Override
public boolean hasAuthInfo(String authId){
return authInfoRepository.hasAuthInfoByAuthId(AuthId.from(authId));
}

@Override
public boolean hasAuthInfo(AuthId authId) {
return authInfoRepository.hasAuthInfoByAuthId(authId);
}
}
15 changes: 15 additions & 0 deletions src/main/java/org/noostak/auth/application/GoogleApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.noostak.auth.application;


import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum GoogleApi {
TOKEN_REQUEST("https://oauth2.googleapis.com/token"),
USER_INFO("https://www.googleapis.com/oauth2/v2/userinfo")
;

private final String url;
}
5 changes: 5 additions & 0 deletions src/main/java/org/noostak/auth/application/GoogleService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.noostak.auth.application;

public interface GoogleService extends OauthService {

}
77 changes: 77 additions & 0 deletions src/main/java/org/noostak/auth/application/GoogleServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.noostak.auth.application;


import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.noostak.auth.application.jwt.JwtToken;
import org.noostak.auth.application.jwt.JwtTokenProvider;
import org.noostak.auth.common.exception.AuthErrorCode;
import org.noostak.auth.common.exception.AuthException;
import org.noostak.auth.domain.vo.AuthId;
import org.noostak.auth.dto.*;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
@Slf4j
public class GoogleServiceImpl implements GoogleService{

private final GoogleTokenRequestFactory googleTokenRequestFactory;
private final RestClient restClient;

@Override
public void requestAccessToken(String refreshToken) {

}


@Override
public TokenInfo fetchTokenInfo(String accessToken) {
return null;
}


@Override
public JwtToken requestToken(String code) {
String url = GoogleApi.TOKEN_REQUEST.getUrl();

GoogleTokenRequest request = googleTokenRequestFactory.createRequest(code);

GoogleTokenResponse response =
restClient.postRequest(url,
request.getUrlEncodedParams(),
GoogleTokenResponse.class);

log.info("googleTokenResponse: {}",response);
response.validate();

return JwtTokenProvider.createToken(response.getAccessToken(),response.getRefreshToken());
}

@Override
public AuthId login(String accessToken) {
String url = GoogleApi.USER_INFO.getUrl();

HttpHeaders headers = makeAuthorizationBearerTokenHeader(accessToken);

GoogleUserInfoResponse response =
restClient.getRequest(url, headers, GoogleUserInfoResponse.class);

response.validate();

return AuthId.from(response.getId());
}

public HttpHeaders makeAuthorizationBearerTokenHeader(String token){
HttpHeaders headers = new HttpHeaders();

if(token == null || token.isEmpty() || token.isBlank()){
throw new AuthException(AuthErrorCode.INVALID_TOKEN);
}

headers.set("Authorization", "Bearer " + token);

return headers;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.noostak.auth.application;

import org.noostak.auth.dto.GoogleTokenRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


@Component
public class GoogleTokenRequestFactory {

@Value("${oauth-property.google.client_id}")
private final String clientId;

@Value("${oauth-property.google.redirect_uri}")
private final String redirectUri;

@Value("${oauth-property.google.client_secret}")
private final String clientSecret;

public GoogleTokenRequestFactory(String clientId, String redirectUri, String clientSecret) {
this.clientId = clientId;
this.redirectUri = redirectUri;
this.clientSecret = clientSecret;
}

public GoogleTokenRequest createRequest(String code) {
return GoogleTokenRequest.of(clientId, redirectUri, code, clientSecret);
}
}
16 changes: 16 additions & 0 deletions src/main/java/org/noostak/auth/application/KaKaoApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.noostak.auth.application;


import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum KaKaoApi {
TOKEN_REQUEST("https://kauth.kakao.com/oauth/token"),
FETCH_TOKEN("https://kapi.kakao.com/v1/user/access_token_info"),
USER_INFO("https://kapi.kakao.com/v2/user/me")
;

private final String url;
}
5 changes: 5 additions & 0 deletions src/main/java/org/noostak/auth/application/KakaoService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.noostak.auth.application;

public interface KakaoService extends OauthService {

}
80 changes: 80 additions & 0 deletions src/main/java/org/noostak/auth/application/KakaoServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package org.noostak.auth.application;


import lombok.RequiredArgsConstructor;
import org.noostak.auth.application.jwt.JwtToken;
import org.noostak.auth.application.jwt.JwtTokenProvider;
import org.noostak.auth.common.exception.AuthErrorCode;
import org.noostak.auth.common.exception.AuthException;
import org.noostak.auth.domain.vo.AuthId;
import org.noostak.auth.dto.*;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class KakaoServiceImpl implements KakaoService{

private final KakaoTokenRequestFactory kakaoTokenRequestFactory;
private final RestClient restClient;
@Override
public void requestAccessToken(String refreshToken) {

}

@Override
public TokenInfo fetchTokenInfo(String accessToken) {
String url = KaKaoApi.FETCH_TOKEN.getUrl();
HttpHeaders headers = makeAuthorizationBearerTokenHeader(accessToken);

KakaoTokenInfoResponse response =
restClient.postRequest(url, headers, KakaoTokenInfoResponse.class);

response.validate();

return TokenInfo.of(response.getId());
}


@Override
public JwtToken requestToken(String code) {
String url = KaKaoApi.TOKEN_REQUEST.getUrl();

KakaoTokenRequest request = kakaoTokenRequestFactory.createRequest(code);

KakaoTokenResponse response =
restClient.postRequest(url,
request.getUrlEncodedParams(),
KakaoTokenResponse.class);

response.validate();

return JwtTokenProvider.createToken(response.getAccessToken(),response.getRefreshToken());
}

@Override
public AuthId login(String accessToken) {
String url = KaKaoApi.USER_INFO.getUrl();

HttpHeaders headers = makeAuthorizationBearerTokenHeader(accessToken);

KakaoUserInfoResponse response =
restClient.postRequest(url, headers, KakaoUserInfoResponse.class);

response.validate();

return AuthId.from(response.getId());
}

public HttpHeaders makeAuthorizationBearerTokenHeader(String token){
HttpHeaders headers = new HttpHeaders();

if(token == null || token.isEmpty() || token.isBlank()){
throw new AuthException(AuthErrorCode.INVALID_TOKEN);
}

headers.set("Authorization", "Bearer " + token);

return headers;
}
}
Loading

0 comments on commit ce3e546

Please sign in to comment.