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

Arch 22 feat/social login #25

Merged
merged 45 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
bfbd02d
chore : OAuth 라이브러리 추가
GaBaljaintheroom Jan 10, 2024
b816280
refact : member 엔티티 수정
GaBaljaintheroom Jan 10, 2024
159c55d
feat : 사용자 역할 속성 추가
GaBaljaintheroom Jan 10, 2024
e3c1e80
feat : 사용자 소셜 타입 추가
GaBaljaintheroom Jan 10, 2024
71ddab0
feat : OAuth 로그인 서비스 작성
GaBaljaintheroom Jan 10, 2024
0673767
feat : OAuth2UserInfo 생성
GaBaljaintheroom Jan 10, 2024
b6fe3a1
feat : 카카오 OAuth2UserInfo 생성
GaBaljaintheroom Jan 10, 2024
347e1aa
feat : 구글 OAuth2UserInfo 생성
GaBaljaintheroom Jan 10, 2024
ad002ca
feat : OAuthAttributes 작성
GaBaljaintheroom Jan 10, 2024
1a9308f
feat : CustomOAuth2User 생성
GaBaljaintheroom Jan 10, 2024
f880081
feat : SecurityConfig에 oauth2Login 추가
GaBaljaintheroom Jan 10, 2024
4599f92
feat : MemberRepository 메서드 추가
GaBaljaintheroom Jan 10, 2024
4c6ed22
feat : OAuthToEntity 메서드 작성
GaBaljaintheroom Jan 10, 2024
587f601
docs : flyway Member 엔티티 수정 사항 작성
GaBaljaintheroom Jan 10, 2024
e7f8b22
chore : local.yml 수정
GaBaljaintheroom Jan 10, 2024
3b3fbc6
feat : CustomOAuth2User final 키워드 추가
GaBaljaintheroom Jan 10, 2024
d12ec72
refact : Role 속성 삭제
GaBaljaintheroom Jan 11, 2024
13960f6
refact : Member 엔티티 수정
GaBaljaintheroom Jan 11, 2024
4c602de
refact : loadUser 메서드 수정
GaBaljaintheroom Jan 11, 2024
8eacb5c
refact : CustomOAuth2User isVerified 필드 추가
GaBaljaintheroom Jan 12, 2024
ac85e0a
refact : createMember.getIsVerified() 추가
GaBaljaintheroom Jan 12, 2024
a7f2e66
style : 패키지 이동
GaBaljaintheroom Jan 12, 2024
31ad9a2
feat : Oauth 성공 핸들러 작성
GaBaljaintheroom Jan 12, 2024
ae241b2
feat : Oauth 실패 핸들러 작성
GaBaljaintheroom Jan 12, 2024
dad4a56
refact : SecurityConfig 성공,실패 핸들러 추가
GaBaljaintheroom Jan 12, 2024
73c5dd9
feat : SocialOauthUri 인테페이스 작성
GaBaljaintheroom Jan 12, 2024
d6a713c
feat : GoogleOauthUri 생성
GaBaljaintheroom Jan 12, 2024
c2a7cfb
feat : KakaoOauthUri 생성
GaBaljaintheroom Jan 12, 2024
dc26b22
feat : auth Controller 작성
GaBaljaintheroom Jan 12, 2024
2a80aa3
chore : application-local.yml 수정
GaBaljaintheroom Jan 12, 2024
2c29e26
fix : 머지 충돌 해결
GaBaljaintheroom Jan 12, 2024
7965151
refact : CustomOAuth2User id 추가
GaBaljaintheroom Jan 12, 2024
b10a903
refact : CustomOAuth2UserService id 추가
GaBaljaintheroom Jan 12, 2024
1450c72
feat : 에러 코드 추가
GaBaljaintheroom Jan 12, 2024
0088f58
refact : 실패 로그 추가
GaBaljaintheroom Jan 12, 2024
d721e00
refact : oauth 인증 실패 수정
GaBaljaintheroom Jan 12, 2024
82c7d77
refact : oauth 성공 핸들러 수정
GaBaljaintheroom Jan 12, 2024
8f89e48
feat : OAuthUrlResponse 생성
GaBaljaintheroom Jan 12, 2024
109a4a5
feat : OAuthDsl 생성
GaBaljaintheroom Jan 12, 2024
5e78c5e
feat : CommonSecurityDsl 생성
GaBaljaintheroom Jan 12, 2024
46f8f9b
refact : AuthController 수정
GaBaljaintheroom Jan 12, 2024
a166993
feat : lombok.config 생성
GaBaljaintheroom Jan 12, 2024
04dd0f5
refact : SecurityConfig 수정
GaBaljaintheroom Jan 12, 2024
03c070f
refact : AuthApi 수정
GaBaljaintheroom Jan 12, 2024
bfe64e7
refact : application-local.yml 수정
GaBaljaintheroom Jan 12, 2024
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
3 changes: 3 additions & 0 deletions backend/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ dependencies {
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

//OAuth
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'

//querydsl
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import site.timecapsulearchive.core.domain.auth.dto.request.TokenReIssueRequest;
import site.timecapsulearchive.core.domain.auth.dto.response.OAuthUrlResponse;
import site.timecapsulearchive.core.domain.auth.dto.response.TemporaryTokenResponse;
import site.timecapsulearchive.core.domain.auth.dto.response.TokenResponse;

Expand All @@ -19,7 +20,7 @@ public interface AuthApi {

@Operation(
summary = "카카오 로그인 페이지",
description = "oauth2 kakao 인증 페이지 url을 가져온다. 로그인에 성공하면 임시 인증 토큰 토큰을 발급받는다.",
description = "oauth2 kakao 인증 페이지 url을 가져온다.",
tags = {"auth"}
)
@ApiResponses(value = {
Expand All @@ -28,20 +29,63 @@ public interface AuthApi {
description = "ok",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = TemporaryTokenResponse.class)
schema = @Schema(implementation = OAuthUrlResponse.class)
)
)
})
@GetMapping(
value = "/auth/login/kakao",
value = "/login/kakao",
produces = {"application/json"}
)
ResponseEntity<TemporaryTokenResponse> getOAuth2KakaoPage();
ResponseEntity<OAuthUrlResponse> getOAuth2KakaoUrl();


@Operation(
summary = "구글 로그인 페이지",
description = "oauth2 google 인증 페이지 url을 가져온다. 로그인에 성공하면 임시 인증 토큰 토큰을 발급받는다.",
description = "oauth2 google 인증 페이지 url을 가져온다.",
tags = {"auth"}
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "ok",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = OAuthUrlResponse.class)
)
)
})
@GetMapping(
value = "/login/google",
produces = {"application/json"}
)
ResponseEntity<OAuthUrlResponse> getOAuth2GoogleUrl();

@Operation(
summary = "카카오 인증 성공시 임시 인증 토큰 발급",
description = "oauth2 kakao 인증 성공시 임시 인증 토큰을 발급한다. (oauth2 로그인 성공시 리다이렉트 엔드포인트로 문서화 목적) ",
tags = {"auth"}
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "ok",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = TemporaryTokenResponse.class)
)
)
})
@GetMapping(
value = "/login/oauth2/code/kakao",
produces = {"application/json"}
)
ResponseEntity<TemporaryTokenResponse> getTemporaryTokenResponseByKakao();


@Operation(
summary = "구글 인증 성공시 임시 인증 토큰 발급",
description = "oauth2 google 인증 성공시 임시 인증 토큰을 발급한다. (oauth2 로그인 성공시 리다이렉트 엔드포인트로 문서화 목적) ",
tags = {"auth"}
)
@ApiResponses(value = {
Expand All @@ -55,10 +99,10 @@ public interface AuthApi {
)
})
@GetMapping(
value = "/auth/login/google",
value = "/login/oauth2/code/google",
produces = {"application/json"}
)
ResponseEntity<TemporaryTokenResponse> getOAuth2GooglePage();
ResponseEntity<TemporaryTokenResponse> getTemporaryTokenResponseByGoogle();


@Operation(
Expand All @@ -77,7 +121,7 @@ public interface AuthApi {
)
})
@PostMapping(
value = "/auth/token/re-issue",
value = "/token/re-issue",
produces = {"application/json"},
consumes = {"application/json"}
)
Expand All @@ -97,7 +141,7 @@ public interface AuthApi {
)
})
@PostMapping(
value = "/auth/verification/send-message",
value = "/verification/send-message",
consumes = {"application/json"}
)
ResponseEntity<Void> sendVerificationMessage();
Expand All @@ -116,7 +160,7 @@ public interface AuthApi {
)
})
@PostMapping(
value = "/auth/verification/valid-message/",
value = "/verification/valid-message/",
produces = {"application/json"},
consumes = {"application/json"}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,38 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import site.timecapsulearchive.core.domain.auth.dto.request.TokenReIssueRequest;
import site.timecapsulearchive.core.domain.auth.dto.response.OAuthUrlResponse;
import site.timecapsulearchive.core.domain.auth.dto.response.TemporaryTokenResponse;
import site.timecapsulearchive.core.domain.auth.dto.response.TokenResponse;
import site.timecapsulearchive.core.domain.auth.service.TokenService;

@RequestMapping("/")
@RestController
@RequiredArgsConstructor
@RequestMapping("/auth")
public class AuthApiController implements AuthApi {

private final TokenService tokenService;

@Override
public ResponseEntity<TemporaryTokenResponse> getOAuth2KakaoPage() {
return null;
public ResponseEntity<OAuthUrlResponse> getOAuth2KakaoUrl() {
throw new UnsupportedOperationException();
}

@Override
public ResponseEntity<OAuthUrlResponse> getOAuth2GoogleUrl() {
throw new UnsupportedOperationException();
}

@Override
public ResponseEntity<TemporaryTokenResponse> getOAuth2GooglePage() {
public ResponseEntity<TemporaryTokenResponse> getTemporaryTokenResponseByKakao() {
return null;
}

@Override
public ResponseEntity<TemporaryTokenResponse> getTemporaryTokenResponseByGoogle() {
throw new UnsupportedOperationException();
}

@Override
public ResponseEntity<TokenResponse> reIssueAccessToken(
@RequestBody final TokenReIssueRequest request) {
Expand All @@ -42,4 +53,4 @@ public ResponseEntity<Void> sendVerificationMessage() {
public ResponseEntity<Void> validVerificationMessage() {
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package site.timecapsulearchive.core.domain.auth.dto.oauth;

import java.util.Collection;
import java.util.Map;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;

public class CustomOAuth2User extends DefaultOAuth2User {

private final boolean isVerified;
private final String email;
private final Long id;

public CustomOAuth2User(
Collection<? extends GrantedAuthority> authorities,
Map<String, Object> attributes,
String nameAttributeKey,
String email,
boolean isVerified,
Long id
) {
super(authorities, attributes, nameAttributeKey);
this.email = email;
this.isVerified = isVerified;
this.id = id;
}

public boolean isNotVerified() {
return !isVerified;
}

public Long getId() {
return id;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package site.timecapsulearchive.core.domain.auth.dto.oauth;

import java.util.Map;

public abstract class OAuth2UserInfo {

protected Map<String, Object> attributes;

protected OAuth2UserInfo(Map<String, Object> attributes) {
this.attributes = attributes;
}

public abstract String getEmail();

public abstract String getImageUrl();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package site.timecapsulearchive.core.domain.auth.dto.oauth;

import java.util.Map;
import lombok.Builder;
import lombok.Getter;
import site.timecapsulearchive.core.domain.auth.dto.oauth.google.GoogleOAuth2UserInfo;
import site.timecapsulearchive.core.domain.auth.dto.oauth.kakao.KakaoOAuth2UserInfo;
import site.timecapsulearchive.core.domain.auth.entity.SocialType;

@Getter
public class OAuthAttributes {

private final String nameAttributeKey;
private final OAuth2UserInfo oauth2UserInfo;

@Builder
private OAuthAttributes(String nameAttributeKey, OAuth2UserInfo oauth2UserInfo) {
this.nameAttributeKey = nameAttributeKey;
this.oauth2UserInfo = oauth2UserInfo;
}

public static OAuthAttributes of(SocialType socialType,
String userNameAttributeName, Map<String, Object> attributes) {
if (socialType == SocialType.KAKAO) {
return ofKakao(userNameAttributeName, attributes);
}
return ofGoogle(userNameAttributeName, attributes);
}

private static OAuthAttributes ofKakao(String userNameAttributeName,
Map<String, Object> attributes) {
return OAuthAttributes.builder()
.nameAttributeKey(userNameAttributeName)
.oauth2UserInfo(new KakaoOAuth2UserInfo(attributes))
.build();
}

public static OAuthAttributes ofGoogle(String userNameAttributeName,
Map<String, Object> attributes) {
return OAuthAttributes.builder()
.nameAttributeKey(userNameAttributeName)
.oauth2UserInfo(new GoogleOAuth2UserInfo(attributes))
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package site.timecapsulearchive.core.domain.auth.dto.oauth;

public interface SocialOauthUri {

String getOauthRedirectURL();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package site.timecapsulearchive.core.domain.auth.dto.oauth.google;

import java.util.Map;
import site.timecapsulearchive.core.domain.auth.dto.oauth.OAuth2UserInfo;

public class GoogleOAuth2UserInfo extends OAuth2UserInfo {

private static final String EMAIL = "email";
private static final String PICTURE = "picture";

public GoogleOAuth2UserInfo(Map<String, Object> attributes) {
super(attributes);
}

@Override
public String getEmail() {
return (String) attributes.get(EMAIL);
}

@Override
public String getImageUrl() {
return (String) attributes.get(PICTURE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package site.timecapsulearchive.core.domain.auth.dto.oauth.google;

import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;
import site.timecapsulearchive.core.domain.auth.dto.oauth.SocialOauthUri;

@Component
@RequiredArgsConstructor
@ConfigurationProperties(prefix = "spring.security.oauth2.client.registration.google")
public class GoogleOauthUri implements SocialOauthUri {

private static final String BASE_URI = "https://accounts.google.com/o/oauth2/v2/auth/oauthchooseaccount";

private String clientId;
private String redirectUri;
private String scope;

@Override
public String getOauthRedirectURL() {
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
queryParams.add("response_type", "code");
queryParams.add("client_id", clientId);
queryParams.add("scope", scope);
queryParams.add("redirect_uri", redirectUri);

return UriComponentsBuilder
.fromUriString(BASE_URI)
.queryParams(queryParams)
.toUriString();
}
}
Loading