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 Grant와 페이지 반환 기능 구현 #18

Merged
merged 20 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 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
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,4 @@ gradle-app.setting
logs/
application-*.yml
src/main/resources/config
!application-test.yml
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ dependencies {

// H2
implementation 'com.h2database:h2'

// Configuration Binding
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
}

tasks.named('test') {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/moabam/MoabamServerApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@ConfigurationPropertiesScan
Copy link
Member

Choose a reason for hiding this comment

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

Q : @ConfigurationPropertiesScan 에 대한 방식은 논의가 필요할 것 같습니다 !

Copy link
Member

Choose a reason for hiding this comment

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

  • 논의 완료
    • 3개 이상 많이 필요할 땐 이 방식으로, 나머지는 @value 사용

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@SpringBootApplication
public class MoabamServerApplication {

public static void main(String[] args) {
SpringApplication.run(MoabamServerApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.moabam.api.application;

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

import java.io.IOException;

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

import com.moabam.api.dto.AuthorizationCodeRequest;
import com.moabam.api.mapper.OAuthMapper;
import com.moabam.global.common.util.GlobalConstant;
import com.moabam.global.config.OAuthConfig;
import com.moabam.global.error.exception.BadRequestException;
import com.moabam.global.error.model.ErrorMessage;

import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class AuthenticationService {

private final OAuthConfig oAuthConfig;

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

private String generateQueryParamsWith(AuthorizationCodeRequest authorizationCodeRequest) {
UriComponentsBuilder authorizationCodeUri = UriComponentsBuilder
.fromUriString(oAuthConfig.provider().authorizationUri())
.queryParam(RESPONSE_TYPE, CODE)
.queryParam(CLIENT_ID, authorizationCodeRequest.clientId())
.queryParam(REDIRECT_URI, authorizationCodeRequest.redirectUri());

if (!authorizationCodeRequest.scope().isEmpty()) {
String scopes = String.join(GlobalConstant.COMMA, authorizationCodeRequest.scope());
authorizationCodeUri.queryParam(SCOPE, scopes);
}

return authorizationCodeUri.toUriString();
}

public void redirectToLoginPage(HttpServletResponse httpServletResponse) {
String authorizationCodeUri = getAuthorizaionCodeUri();

try {
httpServletResponse.setContentType(MediaType.APPLICATION_FORM_URLENCODED + GlobalConstant.CHARSET_UTF_8);
httpServletResponse.sendRedirect(authorizationCodeUri);
} catch (IOException e) {
throw new BadRequestException(ErrorMessage.REQUEST_FAILD);
}
}
}
96 changes: 96 additions & 0 deletions src/main/java/com/moabam/api/domain/Member.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.moabam.api.domain;

import static java.util.Objects.*;

import java.time.LocalDateTime;

import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;

import com.moabam.global.common.entity.BaseTimeEntity;
import com.moabam.global.common.util.BaseImageUrl;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Entity
@Table(name = "member")
@SQLDelete(sql = "UPDATE member SET deleted_at = CURRENT_TIMESTAMP where participant_id = ?")
@Where(clause = "deleted_at IS NOT NULL")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
Copy link
Member

Choose a reason for hiding this comment

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

R : pk는 id로 통일하기로 했던 것 같습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

넵 확인했습니다.


@Column(name = "social_id", nullable = false, unique = true)
private String socialId;

@Column(name = "nickname", nullable = false, unique = true)
private String nickname;

@Column(name = "intro", length = 30)
private String intro;

@Column(name = "profile_image", nullable = false)
private String profileImage;

@Column(name = "total_certify_count", nullable = false)
@ColumnDefault("0")
private long totalCertifyCount;

@Column(name = "report_count", nullable = false)
@ColumnDefault("0")
private int reportCount;

@Column(name = "current_night_count", nullable = false)
@ColumnDefault("0")
private int currentNightCount;

@Column(name = "current_morning_count", nullable = false)
@ColumnDefault("0")
private int currentMorningCount;

@Column(name = "morning_bug", nullable = false)
@ColumnDefault("0")
private int morningBug;

@Column(name = "night_bug", nullable = false)
@ColumnDefault("0")
private int nightBug;

@Column(name = "golden_bug", nullable = false)
@ColumnDefault("0")
private int goldenBug;

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

@Column(name = "deleted_at")
Copy link
Member

Choose a reason for hiding this comment

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

R: deleted_at이 member에서는 hard delete인가요 !?
soft delete라면 @SQLdelete 추가 필요해보입니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@SQLdelete 논의가 아직 끝나지 않았었던 시기라, 곧 적용하겠습니다!

private LocalDateTime deletedAt;
Copy link
Member

Choose a reason for hiding this comment

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

R: @SQLDelete 적용! 그리고 삭제된 회원은 관리자가 아닌 이상 접근할 필요가 없으니까 @where도 넣어주면 좋을것 같아요!


@Builder
private Member(Long id, String socialId, String nickname, String profileImage) {
this.id = id;
this.socialId = requireNonNull(socialId);
this.nickname = requireNonNull(nickname);
this.profileImage = requireNonNullElse(profileImage, BaseImageUrl.PROFILE_URL);
this.role = Role.USER;
}
}
8 changes: 8 additions & 0 deletions src/main/java/com/moabam/api/domain/Role.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.moabam.api.domain;

public enum Role {

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

import static java.util.Objects.*;

import java.util.List;

import lombok.Builder;

public record AuthorizationCodeRequest(
String clientId,
String redirectUri,
String responseType,
List<String> scope,
String state
) {

@Builder
public AuthorizationCodeRequest(String clientId, String redirectUri, String responseType, List<String> scope,
String state) {
this.clientId = requireNonNull(clientId);
this.redirectUri = requireNonNull(redirectUri);
this.responseType = responseType;
this.scope = scope;
this.state = state;
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/moabam/api/mapper/OAuthMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.moabam.api.mapper;

import com.moabam.api.dto.AuthorizationCodeRequest;
import com.moabam.global.config.OAuthConfig;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

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

public static AuthorizationCodeRequest toAuthorizationCodeRequest(OAuthConfig oAuthConfig) {
return AuthorizationCodeRequest.builder()
.clientId(oAuthConfig.client().clientId())
.redirectUri(oAuthConfig.provider().redirectUri())
.scope(oAuthConfig.client().scope())
.build();
}
}
23 changes: 23 additions & 0 deletions src/main/java/com/moabam/api/presentation/MemberController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.moabam.api.presentation;

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

import com.moabam.api.application.AuthenticationService;

import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/members")
@RequiredArgsConstructor
public class MemberController {

private final AuthenticationService authenticationService;

@GetMapping
public void socialLogin(HttpServletResponse httpServletResponse) {
authenticationService.redirectToLoginPage(httpServletResponse);
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/moabam/global/common/util/BaseImageUrl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.moabam.global.common.util;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

Copy link
Member

Choose a reason for hiding this comment

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

C: Global Constant처럼 public static final로 하는거는 어떤가요? Enum으로 하신 이유가 무엇인가요!?

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

public static final String PROFILE_URL = "/profile/baseUrl";
}
11 changes: 11 additions & 0 deletions src/main/java/com/moabam/global/common/util/GlobalConstant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.moabam.global.common.util;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

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

public static final String COMMA = ",";
public static final String CHARSET_UTF_8 = ";charset=UTF-8";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.moabam.global.common.util;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

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

public static final String RESPONSE_TYPE = "response_type";
public static final String CODE = "code";
public static final String CLIENT_ID = "client_id";
public static final String REDIRECT_URI = "redirect_uri";
public static final String SCOPE = "scope";
Copy link
Member

Choose a reason for hiding this comment

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

C : 매직넘버를 빼려는 의도는 알겠으나, 과도한 static 사용 시 메모리에 대한 걱정을 해야할 것 같아요.
이정도는 그냥 작성해도 괜찮지 않았나.. 하는 생각입니다 !

}
Copy link
Member

Choose a reason for hiding this comment

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

C: 대소문자만 차이가 나서 굳이 하는게 좋을까 생각이 듭니다!

28 changes: 28 additions & 0 deletions src/main/java/com/moabam/global/config/OAuthConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.moabam.global.config;

import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "oauth2")
public record OAuthConfig(
Provider provider,
Client client
) {

public record Client(
String provider,
String clientId,
String authorizationGrantType,
List<String> scope
) {

}

public record Provider(
String authorizationUri,
String redirectUri
) {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
@RequiredArgsConstructor
public enum ErrorMessage {

INVALID_REQUEST_FIELD("올바른 요청 정보가 아닙니다.");
INVALID_REQUEST_FIELD("올바른 요청 정보가 아닙니다."),
LOGIN_FAILED("로그인에 실패했습니다."),
REQUEST_FAILD("네트우크 접근 실패입니다.");

private final String message;
}
2 changes: 1 addition & 1 deletion src/main/resources/config
Loading