Skip to content

Commit

Permalink
feat: social 회원 토큰 조회 기능 추가 (#37)
Browse files Browse the repository at this point in the history
* feat: 회원 엔티티 생성 및 테스트코드 추가

* feat: 카카오 OAuth 환경변수 추가 및 클래스 바인딩

* feat: authorization code를 받기 위한 queryString generator 추가

* feat: Authorization code의 parameter 만드는 로직 분리 및 테스트 코드 추가

* feat: 회원 가입/로그인 요청 api 및 소셜 로그인 페이지 반환

* refactor: member관련 클래스 네이밍과 폴더 위치 변경

* refactor: 로그인 페이지 요청 방식 Resttemplate -> response (redirect)하도록 변경

* style: 코드 포맷 재적용 및 사용하지 않는 클래스 삭제

* chore: config 파일 업데이트

* refactor: 테스트 코드 추가 및 코드 포맷 재적용

* refactor: 사용하지 않는 코드 제거

* refactor: CRLF -> LF로 변경

* fix: config 커밋, config 최근 커밋으로 변경

* feat: 테스트 코드 추가 및 패키지 구조 변경

* refactor: revert merge

* fix: merge confilt해결 및 예외처리 추가

* test: oauth properties가 없을 때의 테스트코드 추가

* feat: 코드리뷰에 따른 기능 분리 및 테스트 코드 변경

* fix: 테스트코드 관련 code smell 제거

* feat: Authorization grant 받기 예외 코드 및 테스트 코드 추가

* feat: Authorization Token 요청 및 반환 코드, 에러 반환 테스트 코드 추가

* refactor: AuthenticationService에서 서버에 요청보내는 로직 OAuth2AuthorizationServerRequestService로 분리

* test: 로그인 요청 테스트 코드 추가

* feat: 토큰 발급 요청 기능 테스트 코드 추가 및 RestTemplate 필드변수로 변경

* test: restTemplate 및 서비스 테스트 추가

* refactor: 에러 메세지 이름 변경

* refacotr: 변수명 및 entity default 명 변경

* feat: 토큰 정보 조회 기능 및 테스트 추가

* feat: 사용자 토큰 정보 조회 및 테스트 코드 & Resttemplate 테크트 코드 변경

* fix: encoding, formatting, tab 문제로 인한 파일 삭제 후 다시 작성

* fix: 코드 리뷰 반영
  • Loading branch information
parksey authored Nov 6, 2023
1 parent 7ad2629 commit db829cb
Show file tree
Hide file tree
Showing 13 changed files with 337 additions and 72 deletions.
35 changes: 24 additions & 11 deletions src/main/java/com/moabam/api/application/AuthenticationService.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@

import com.moabam.api.dto.AuthorizationCodeRequest;
import com.moabam.api.dto.AuthorizationCodeResponse;
import com.moabam.api.dto.AuthorizationTokenInfoResponse;
import com.moabam.api.dto.AuthorizationTokenRequest;
import com.moabam.api.dto.AuthorizationTokenResponse;
import com.moabam.api.dto.OAuthMapper;
import com.moabam.global.common.util.GlobalConstant;
import com.moabam.global.common.util.TokenConstant;
import com.moabam.global.config.OAuthConfig;
import com.moabam.global.error.exception.BadRequestException;
import com.moabam.global.error.model.ErrorMessage;
Expand All @@ -28,11 +30,33 @@ public class AuthenticationService {
private final OAuthConfig oAuthConfig;
private final OAuth2AuthorizationServerRequestService oauth2AuthorizationServerRequestService;

public void redirectToLoginPage(HttpServletResponse httpServletResponse) {
String authorizationCodeUri = getAuthorizationCodeUri();
oauth2AuthorizationServerRequestService.loginRequest(httpServletResponse, authorizationCodeUri);
}

public AuthorizationTokenResponse requestToken(AuthorizationCodeResponse authorizationCodeResponse) {
validAuthorizationGrant(authorizationCodeResponse.code());
return issueTokenToAuthorizationServer(authorizationCodeResponse.code());
}

public AuthorizationTokenInfoResponse requestTokenInfo(AuthorizationTokenResponse authorizationTokenResponse) {
String tokenValue = generateTokenValue(authorizationTokenResponse.accessToken());
ResponseEntity<AuthorizationTokenInfoResponse> authorizationTokenInfoResponse
= oauth2AuthorizationServerRequestService.tokenInfoRequest(oAuthConfig.provider().tokenInfo(), tokenValue);

return authorizationTokenInfoResponse.getBody();
}

private String getAuthorizationCodeUri() {
AuthorizationCodeRequest authorizationCodeRequest = OAuthMapper.toAuthorizationCodeRequest(oAuthConfig);
return generateQueryParamsWith(authorizationCodeRequest);
}

private String generateTokenValue(String token) {
return TokenConstant.TOKEN_TYPE + GlobalConstant.SPACE + token;
}

private String generateQueryParamsWith(AuthorizationCodeRequest authorizationCodeRequest) {
UriComponentsBuilder authorizationCodeUri = UriComponentsBuilder
.fromUriString(oAuthConfig.provider().authorizationUri())
Expand Down Expand Up @@ -78,15 +102,4 @@ private MultiValueMap<String, String> generateTokenRequest(AuthorizationTokenReq

return contents;
}

public void redirectToLoginPage(HttpServletResponse httpServletResponse) {
String authorizationCodeUri = getAuthorizationCodeUri();
oauth2AuthorizationServerRequestService.loginRequest(httpServletResponse, authorizationCodeUri);
}

public void requestToken(AuthorizationCodeResponse authorizationCodeResponse) {
validAuthorizationGrant(authorizationCodeResponse.code());
issueTokenToAuthorizationServer(authorizationCodeResponse.code());
// TODO 발급한 토큰으로 사용자의 정보 얻어와야함 : 프로필 & 닉네임
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.IOException;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
Expand All @@ -11,9 +12,12 @@
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import com.moabam.api.dto.AuthorizationTokenInfoResponse;
import com.moabam.api.dto.AuthorizationTokenResponse;
import com.moabam.global.common.util.GlobalConstant;
import com.moabam.global.common.util.TokenConstant;
import com.moabam.global.error.exception.BadRequestException;
import com.moabam.global.error.handler.RestTemplateResponseHandler;
import com.moabam.global.error.model.ErrorMessage;

import jakarta.servlet.http.HttpServletResponse;
Expand All @@ -24,7 +28,9 @@ public class OAuth2AuthorizationServerRequestService {
private final RestTemplate restTemplate;

public OAuth2AuthorizationServerRequestService() {
restTemplate = new RestTemplate();
restTemplate = new RestTemplateBuilder()
.errorHandler(new RestTemplateResponseHandler())
.build();
}

public void loginRequest(HttpServletResponse httpServletResponse, String authorizationCodeUri) {
Expand All @@ -43,13 +49,14 @@ public ResponseEntity<AuthorizationTokenResponse> requestAuthorizationServer(Str
MediaType.APPLICATION_FORM_URLENCODED_VALUE + GlobalConstant.CHARSET_UTF_8);
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(uriParams, headers);

ResponseEntity<AuthorizationTokenResponse> authorizationTokenResponse = restTemplate.exchange(tokenUri,
HttpMethod.POST, httpEntity, AuthorizationTokenResponse.class);
return restTemplate.exchange(tokenUri, HttpMethod.POST, httpEntity, AuthorizationTokenResponse.class);
}

if (authorizationTokenResponse.getStatusCode().isError()) {
throw new BadRequestException(ErrorMessage.REQUEST_FAILED);
}
public ResponseEntity<AuthorizationTokenInfoResponse> tokenInfoRequest(String tokenInfoUri, String tokenValue) {
HttpHeaders headers = new HttpHeaders();
headers.add(TokenConstant.AUTHORIZATION, tokenValue);
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);

return authorizationTokenResponse;
return restTemplate.exchange(tokenInfoUri, HttpMethod.GET, httpEntity, AuthorizationTokenInfoResponse.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.moabam.api.dto;

import com.fasterxml.jackson.annotation.JsonProperty;

public record AuthorizationTokenInfoResponse(
@JsonProperty("id") long id,
@JsonProperty("expires_in") String expiresIn,
@JsonProperty("app_id") String appId
) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import com.moabam.api.application.AuthenticationService;
import com.moabam.api.dto.AuthorizationCodeResponse;
import com.moabam.api.dto.AuthorizationTokenResponse;

import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
Expand All @@ -25,6 +26,7 @@ public void socialLogin(HttpServletResponse httpServletResponse) {

@GetMapping("/login/kakao/oauth")
public void authorizationTokenIssue(@ModelAttribute AuthorizationCodeResponse authorizationCodeResponse) {
authenticationService.requestToken(authorizationCodeResponse);
AuthorizationTokenResponse tokenResponse = authenticationService.requestToken(authorizationCodeResponse);
authenticationService.requestTokenInfo(tokenResponse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class GlobalConstant {
public static final String COMMA = ",";
public static final String UNDER_BAR = "_";
public static final String CHARSET_UTF_8 = ";charset=UTF-8";
public static final String SPACE = " ";

public static final String TO = "_TO_";
public static final long EXPIRE_KNOCK = 12;
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/moabam/global/common/util/TokenConstant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.moabam.global.common.util;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TokenConstant {

public static final String TOKEN_TYPE = "Bearer";
public static final String ACCESS_TOKEN = "access_token";
public static final String REFRESH_TOKEN = "refresh_token";
public static final String AUTHORIZATION = "Authorization";
}
3 changes: 2 additions & 1 deletion src/main/java/com/moabam/global/config/OAuthConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public record Client(
public record Provider(
String authorizationUri,
String redirectUri,
String tokenUri
String tokenUri,
String tokenInfo
) {

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.moabam.global.error.handler;

import java.io.IOException;

import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.ResponseErrorHandler;

import com.moabam.global.error.exception.BadRequestException;
import com.moabam.global.error.model.ErrorMessage;

@Component
public class RestTemplateResponseHandler implements ResponseErrorHandler {

@Override
public boolean hasError(ClientHttpResponse response) {
try {
return response.getStatusCode().isError();
} catch (IOException ioException) {
throw new BadRequestException(ErrorMessage.REQUEST_FAILED);
}
}

@Override
public void handleError(ClientHttpResponse response) {
try {
HttpStatusCode statusCode = response.getStatusCode();
validResponse(statusCode);
} catch (IOException ioException) {
throw new BadRequestException(ErrorMessage.REQUEST_FAILED);
}
}

private void validResponse(HttpStatusCode statusCode) {
if (statusCode.is5xxServerError()) {
throw new BadRequestException(ErrorMessage.REQUEST_FAILED);
}

if (statusCode.is4xxClientError()) {
throw new BadRequestException(ErrorMessage.INVALID_REQUEST_FIELD);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@

import com.moabam.api.dto.AuthorizationCodeRequest;
import com.moabam.api.dto.AuthorizationCodeResponse;
import com.moabam.api.dto.AuthorizationTokenInfoResponse;
import com.moabam.api.dto.AuthorizationTokenRequest;
import com.moabam.api.dto.AuthorizationTokenResponse;
import com.moabam.api.dto.OAuthMapper;
import com.moabam.fixture.AuthorizationTokenResponseFixture;
import com.moabam.fixture.AuthorizationResponseFixture;
import com.moabam.global.config.OAuthConfig;
import com.moabam.global.error.exception.BadRequestException;
import com.moabam.global.error.model.ErrorMessage;
Expand All @@ -46,14 +47,15 @@ class AuthenticationServiceTest {
@BeforeEach
public void initParams() {
oauthConfig = new OAuthConfig(
new OAuthConfig.Provider("https://authorization/url", "http://redirect/url", "http://token/url"),
new OAuthConfig.Provider("https://authorization/url", "http://redirect/url", "http://token/url",
"http://tokenInfo/url"),
new OAuthConfig.Client("provider", "testtestetsttest", "testtesttest", "authorization_code",
List.of("profile_nickname", "profile_image"))
);
ReflectionTestUtils.setField(authenticationService, "oAuthConfig", oauthConfig);

noOAuthConfig = new OAuthConfig(
new OAuthConfig.Provider(null, null, null),
new OAuthConfig.Provider(null, null, null, null),
new OAuthConfig.Client(null, null, null, null, null)
);
noPropertyService = new AuthenticationService(noOAuthConfig, oAuth2AuthorizationServerRequestService);
Expand Down Expand Up @@ -115,7 +117,7 @@ void authorization_grant_success() {
AuthorizationCodeResponse authorizationCodeResponse = new AuthorizationCodeResponse("test", null,
null, null);
AuthorizationTokenResponse authorizationTokenResponse =
AuthorizationTokenResponseFixture.authorizationTokenResponse();
AuthorizationResponseFixture.authorizationTokenResponse();

// When
when(oAuth2AuthorizationServerRequestService.requestAuthorizationServer(anyString(), any())).thenReturn(
Expand Down Expand Up @@ -157,4 +159,21 @@ void token_request_mapping_success() {
() -> assertThat(authorizationTokenRequest.code()).isEqualTo(code)
);
}

@DisplayName("토큰 변경 성공")
@Test
void generate_token() {
// Given
AuthorizationTokenResponse tokenResponse = AuthorizationResponseFixture.authorizationTokenResponse();
AuthorizationTokenInfoResponse tokenInfoResponse
= AuthorizationResponseFixture.authorizationTokenInfoResponse();

// When
when(oAuth2AuthorizationServerRequestService.tokenInfoRequest(eq(oauthConfig.provider().tokenInfo()),
eq("Bearer " + tokenResponse.accessToken())))
.thenReturn(new ResponseEntity<>(tokenInfoResponse, HttpStatus.OK));

// Then
assertThatNoException().isThrownBy(() -> authenticationService.requestTokenInfo(tokenResponse));
}
}
Loading

0 comments on commit db829cb

Please sign in to comment.