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

feat: Authorization Server로 부터 토큰 발급 기능 추가 #24

Merged
merged 30 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2cb3959
feat: 회원 엔티티 생성 및 테스트코드 추가
parksey Oct 29, 2023
3c119f8
feat: 카카오 OAuth 환경변수 추가 및 클래스 바인딩
parksey Oct 29, 2023
729d7d2
feat: authorization code를 받기 위한 queryString generator 추가
parksey Oct 29, 2023
9cbfc2e
feat: Authorization code의 parameter 만드는 로직 분리 및 테스트 코드 추가
parksey Oct 30, 2023
e056905
feat: 회원 가입/로그인 요청 api 및 소셜 로그인 페이지 반환
parksey Oct 30, 2023
0dc50e4
refactor: member관련 클래스 네이밍과 폴더 위치 변경
parksey Oct 30, 2023
233661a
refactor: 로그인 페이지 요청 방식 Resttemplate -> response (redirect)하도록 변경
parksey Oct 30, 2023
18f3496
style: 코드 포맷 재적용 및 사용하지 않는 클래스 삭제
parksey Oct 30, 2023
a1e7533
chore: config 파일 업데이트
parksey Oct 30, 2023
b5163eb
refactor: 테스트 코드 추가 및 코드 포맷 재적용
parksey Oct 30, 2023
9c10d45
refactor: 사용하지 않는 코드 제거
parksey Oct 30, 2023
92cb531
refactor: CRLF -> LF로 변경
parksey Oct 30, 2023
a7291a8
fix: config 커밋, config 최근 커밋으로 변경
parksey Oct 30, 2023
47ef3ea
feat: 테스트 코드 추가 및 패키지 구조 변경
parksey Oct 30, 2023
5a76a50
refactor: revert merge
parksey Oct 30, 2023
ab0063d
Merge branch 'develop' into feature/#5
parksey Oct 30, 2023
ab0b0ab
fix: merge confilt해결 및 예외처리 추가
parksey Oct 30, 2023
dff5e2e
test: oauth properties가 없을 때의 테스트코드 추가
parksey Oct 30, 2023
31407ab
feat: 코드리뷰에 따른 기능 분리 및 테스트 코드 변경
parksey Oct 31, 2023
448011e
fix: 테스트코드 관련 code smell 제거
parksey Oct 31, 2023
c421394
feat: Authorization grant 받기 예외 코드 및 테스트 코드 추가
parksey Nov 1, 2023
40cac6f
refactor: develop브랜치 merge
parksey Nov 1, 2023
36a64b1
feat: Authorization Token 요청 및 반환 코드, 에러 반환 테스트 코드 추가
parksey Nov 1, 2023
1eb8eb4
refactor: AuthenticationService에서 서버에 요청보내는 로직 OAuth2AuthorizationSer…
parksey Nov 1, 2023
5ef8308
test: 로그인 요청 테스트 코드 추가
parksey Nov 1, 2023
82925fa
feat: 토큰 발급 요청 기능 테스트 코드 추가 및 RestTemplate 필드변수로 변경
parksey Nov 1, 2023
95bea5d
refactor: develop 브랜치 merge
parksey Nov 1, 2023
7e53464
test: restTemplate 및 서비스 테스트 추가
parksey Nov 2, 2023
41b234b
refactor: 에러 메세지 이름 변경
parksey Nov 2, 2023
dffed00
refacotr: 변수명 및 entity default 명 변경
parksey Nov 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

R: 제가 다음 PR에 Apache Commons Lang 3 추가했습니다.
StringUtils.isEmpty()로 쓰면 String을 null safe하게 검증할 수 있습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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`")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 백틱이 아니라 작은 따옴표 써야하지 않나여?

Suggested change
@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--;
}
Comment on lines +90 to +108
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

굳 넣어주셔서 감사합니다~

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵!

}
}
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 stats
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 혹시 오타인지? 아님말구~

) {

}
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