Skip to content

Commit

Permalink
feat: Authorization Server로 부터 토큰 발급 기능 추가 (#24)
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 명 변경
  • Loading branch information
parksey authored Nov 2, 2023
1 parent e1484c7 commit 7c252fe
Show file tree
Hide file tree
Showing 17 changed files with 562 additions and 70 deletions.
57 changes: 46 additions & 11 deletions src/main/java/com/moabam/api/application/AuthenticationService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

import static com.moabam.global.common.util.OAuthParameterNames.*;

import java.io.IOException;

import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;

import com.moabam.api.dto.AuthorizationCodeRequest;
import com.moabam.api.dto.AuthorizationCodeResponse;
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.config.OAuthConfig;
Expand All @@ -23,8 +26,9 @@
public class AuthenticationService {

private final OAuthConfig oAuthConfig;
private final OAuth2AuthorizationServerRequestService oauth2AuthorizationServerRequestService;

private String getAuthorizaionCodeUri() {
private String getAuthorizationCodeUri() {
AuthorizationCodeRequest authorizationCodeRequest = OAuthMapper.toAuthorizationCodeRequest(oAuthConfig);
return generateQueryParamsWith(authorizationCodeRequest);
}
Expand All @@ -44,14 +48,45 @@ private String generateQueryParamsWith(AuthorizationCodeRequest authorizationCod
return authorizationCodeUri.toUriString();
}

public void redirectToLoginPage(HttpServletResponse httpServletResponse) {
String authorizationCodeUri = getAuthorizaionCodeUri();
private void validAuthorizationGrant(String code) {
if (code == null) {
throw new BadRequestException(ErrorMessage.GRANT_FAILED);
}
}

private AuthorizationTokenResponse issueTokenToAuthorizationServer(String code) {
AuthorizationTokenRequest authorizationTokenRequest = OAuthMapper.toAuthorizationTokenRequest(oAuthConfig,
code);
MultiValueMap<String, String> uriParams = generateTokenRequest(authorizationTokenRequest);
ResponseEntity<AuthorizationTokenResponse> authorizationTokenResponse =
oauth2AuthorizationServerRequestService.requestAuthorizationServer(oAuthConfig.provider().tokenUri(),
uriParams);

return authorizationTokenResponse.getBody();
}

try {
httpServletResponse.setContentType(MediaType.APPLICATION_FORM_URLENCODED + GlobalConstant.CHARSET_UTF_8);
httpServletResponse.sendRedirect(authorizationCodeUri);
} catch (IOException e) {
throw new BadRequestException(ErrorMessage.REQUEST_FAILD);
private MultiValueMap<String, String> generateTokenRequest(AuthorizationTokenRequest authorizationTokenRequest) {
MultiValueMap<String, String> contents = new LinkedMultiValueMap<>();
contents.add(GRANT_TYPE, authorizationTokenRequest.grantType());
contents.add(CLIENT_ID, authorizationTokenRequest.clientId());
contents.add(REDIRECT_URI, authorizationTokenRequest.redirectUri());
contents.add(CODE, authorizationTokenRequest.code());

if (authorizationTokenRequest.clientSecret() != null) {
contents.add(CLIENT_SECRET, authorizationTokenRequest.clientSecret());
}

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
@@ -0,0 +1,55 @@
package com.moabam.api.application;

import java.io.IOException;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

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

import jakarta.servlet.http.HttpServletResponse;

@Service
public class OAuth2AuthorizationServerRequestService {

private final RestTemplate restTemplate;

public OAuth2AuthorizationServerRequestService() {
restTemplate = new RestTemplate();
}

public void loginRequest(HttpServletResponse httpServletResponse, String authorizationCodeUri) {
try {
httpServletResponse.setContentType(MediaType.APPLICATION_FORM_URLENCODED + GlobalConstant.CHARSET_UTF_8);
httpServletResponse.sendRedirect(authorizationCodeUri);
} catch (IOException e) {
throw new BadRequestException(ErrorMessage.REQUEST_FAILED);
}
}

public ResponseEntity<AuthorizationTokenResponse> requestAuthorizationServer(String tokenUri,
MultiValueMap<String, String> uriParams) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE,
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);

if (authorizationTokenResponse.getStatusCode().isError()) {
throw new BadRequestException(ErrorMessage.REQUEST_FAILED);
}

return authorizationTokenResponse;
}
}
24 changes: 22 additions & 2 deletions src/main/java/com/moabam/api/domain/entity/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
@Entity
@Getter
@Table(name = "member")
@SQLDelete(sql = "UPDATE member SET deleted_at = CURRENT_TIMESTAMP where participant_id = ?")
@SQLDelete(sql = "UPDATE member SET deleted_at = CURRENT_TIMESTAMP where id = ?")
@Where(clause = "deleted_at IS NOT NULL")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member extends BaseTimeEntity {
Expand Down Expand Up @@ -72,7 +72,7 @@ public class Member extends BaseTimeEntity {

@Enumerated(EnumType.STRING)
@Column(name = "role", nullable = false)
@ColumnDefault("USER")
@ColumnDefault("'USER'")
private Role role;

@Column(name = "deleted_at")
Expand All @@ -87,4 +87,24 @@ private Member(Long id, String socialId, String nickname, String profileImage, B
this.bug = requireNonNull(bug);
this.role = Role.USER;
}

public void enterMorningRoom() {
currentMorningCount++;
}

public void enterNightRoom() {
currentNightCount++;
}

public void exitMorningRoom() {
if (currentMorningCount > 0) {
currentMorningCount--;
}
}

public void exitNightRoom() {
if (currentMorningCount > 0) {
currentNightCount--;
}
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/moabam/api/dto/AuthorizationCodeResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.moabam.api.dto;

public record AuthorizationCodeResponse(
String code,
String error,
String errorDescription,
String state
) {

}
24 changes: 24 additions & 0 deletions src/main/java/com/moabam/api/dto/AuthorizationTokenRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.moabam.api.dto;

import static java.util.Objects.*;

import lombok.Builder;

public record AuthorizationTokenRequest(
String grantType,
String clientId,
String redirectUri,
String code,
String clientSecret
) {

@Builder
public AuthorizationTokenRequest(String grantType, String clientId, String redirectUri, String code,
String clientSecret) {
this.grantType = requireNonNull(grantType);
this.clientId = requireNonNull(clientId);
this.redirectUri = requireNonNull(redirectUri);
this.code = requireNonNull(code);
this.clientSecret = clientSecret;
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/moabam/api/dto/AuthorizationTokenResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.moabam.api.dto;

import com.fasterxml.jackson.annotation.JsonProperty;

public record AuthorizationTokenResponse(
@JsonProperty("token_type") String tokenType,
@JsonProperty("access_token") String accessToken,
@JsonProperty("id_token") String idToken,
@JsonProperty("expires_in") String expiresIn,
@JsonProperty("refresh_token") String refreshToken,
@JsonProperty("refresh_token_expires_in") String refreshTokenExpiresIn,
@JsonProperty("scope") String scope
) {

}
10 changes: 10 additions & 0 deletions src/main/java/com/moabam/api/dto/OAuthMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,14 @@ public static AuthorizationCodeRequest toAuthorizationCodeRequest(OAuthConfig oA
.scope(oAuthConfig.client().scope())
.build();
}

public static AuthorizationTokenRequest toAuthorizationTokenRequest(OAuthConfig oAuthConfig, String code) {
return AuthorizationTokenRequest.builder()
.grantType(oAuthConfig.client().authorizationGrantType())
.clientId(oAuthConfig.client().clientId())
.redirectUri(oAuthConfig.provider().redirectUri())
.code(code)
.clientSecret(oAuthConfig.client().clientSecret())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.moabam.api.presentation;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
Expand All @@ -20,4 +22,9 @@ public class MemberController {
public void socialLogin(HttpServletResponse httpServletResponse) {
authenticationService.redirectToLoginPage(httpServletResponse);
}

@GetMapping("/login/kakao/oauth")
public void authorizationTokenIssue(@ModelAttribute AuthorizationCodeResponse authorizationCodeResponse) {
authenticationService.requestToken(authorizationCodeResponse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ public class OAuthParameterNames {
public static final String CLIENT_ID = "client_id";
public static final String REDIRECT_URI = "redirect_uri";
public static final String SCOPE = "scope";
public static final String GRANT_TYPE = "grant_type";
public static final String CLIENT_SECRET = "client_secret";
}
4 changes: 3 additions & 1 deletion src/main/java/com/moabam/global/config/OAuthConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public record OAuthConfig(
public record Client(
String provider,
String clientId,
String clientSecret,
String authorizationGrantType,
List<String> scope
) {
Expand All @@ -21,7 +22,8 @@ public record Client(

public record Provider(
String authorizationUri,
String redirectUri
String redirectUri,
String tokenUri
) {

}
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/moabam/global/error/model/ErrorMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
public enum ErrorMessage {

INVALID_REQUEST_FIELD("올바른 요청 정보가 아닙니다."),

ROOM_NOT_FOUND("존재하지 않는 방 입니다."),
ROOM_MAX_USER_COUNT_MODIFY_FAIL("잘못된 최대 인원수 설정입니다."),
ROOM_MODIFY_UNAUTHORIZED_REQUEST("방장이 아닌 사용자는 방을 수정할 수 없습니다."),
PARTICIPANT_NOT_FOUND("방에 대한 참여자의 정보가 없습니다."),
LOGIN_FAILED("로그인에 실패했습니다."),
REQUEST_FAILD("네트우크 접근 실패입니다."),

LOGIN_FAILED("로그인에 실패했습니다."),
REQUEST_FAILED("네트워크 접근 실패입니다."),
GRANT_FAILED("인가 코드 실패"),
MEMBER_NOT_FOUND("존재하지 않는 회원입니다."),

INVALID_BUG_COUNT("벌레 개수는 0 이상이어야 합니다."),
Expand Down
Loading

0 comments on commit 7c252fe

Please sign in to comment.