Skip to content

Commit 2a49e65

Browse files
authored
Merge pull request #251 from PawWithU/feat/248-apple-login
[Feature] Spring Security를 이용한 OAuth 2.0(APPLE) 로그인 구현
2 parents 85c4d98 + 28f2e67 commit 2a49e65

18 files changed

+379
-141
lines changed

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ dependencies {
6767
implementation 'com.google.firebase:firebase-admin:6.8.1'
6868
implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.2.2'
6969
implementation 'com.squareup.okio:okio:2.7.0'
70+
// AuthO
71+
implementation 'com.auth0:java-jwt:3.19.2'
72+
implementation 'com.auth0:jwks-rsa:0.21.1'
7073
}
7174

7275
// Querydsl 설정부
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.pawwithu.connectdog.domain.oauth.api;
2+
3+
import com.pawwithu.connectdog.domain.oauth.dto.response.OAuthInfoResponse;
4+
import com.pawwithu.connectdog.domain.oauth.service.AppleIdTokenDecodeService;
5+
import com.pawwithu.connectdog.domain.volunteer.entity.SocialType;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.stereotype.Component;
8+
import org.springframework.web.client.RestTemplate;
9+
10+
@Component
11+
@RequiredArgsConstructor
12+
public class AppleApiClient implements OAuthApiClient {
13+
14+
private final AppleIdTokenDecodeService appleIdTokenDecodeService;
15+
16+
private String apiUrl = "https://kapi.kakao.com";
17+
private final RestTemplate restTemplate;
18+
19+
@Override
20+
public SocialType socialType() {
21+
return SocialType.APPLE;
22+
}
23+
24+
@Override
25+
public OAuthInfoResponse requestOauthInfo(String socialToken) {
26+
return appleIdTokenDecodeService.getPayloadFromIdToken(socialToken);
27+
}
28+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.pawwithu.connectdog.domain.oauth.api;
2+
3+
import com.pawwithu.connectdog.domain.oauth.dto.response.OidcPublicKeyListResponse;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.stereotype.Component;
6+
import org.springframework.web.client.RestTemplate;
7+
8+
@Component
9+
@RequiredArgsConstructor
10+
public class AppleOidcKeyClient {
11+
12+
private final RestTemplate restTemplate;
13+
14+
public OidcPublicKeyListResponse getAppleOidcOpenKeys() {
15+
String appleOidcPublicKeyUrl = "https://appleid.apple.com/auth/keys";
16+
return restTemplate.getForObject(appleOidcPublicKeyUrl, OidcPublicKeyListResponse.class);
17+
}
18+
}

src/main/java/com/pawwithu/connectdog/domain/oauth/api/KakaoApiClient.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ public SocialType socialType() {
2424
}
2525

2626
@Override
27-
public OAuthInfoResponse requestOauthInfo(String accessToken) {
27+
public OAuthInfoResponse requestOauthInfo(String socialToken) {
2828
String url = apiUrl + "/v2/user/me";
2929

3030
HttpHeaders httpHeaders = new HttpHeaders();
3131
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
32-
httpHeaders.set("Authorization", "Bearer " + accessToken);
32+
httpHeaders.set("Authorization", "Bearer " + socialToken);
3333

3434
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
3535
body.add("property_keys", "[\"id\"]"); // id 값만 받아옴

src/main/java/com/pawwithu/connectdog/domain/oauth/api/NaverApiClient.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ public SocialType socialType() {
2424
}
2525

2626
@Override
27-
public OAuthInfoResponse requestOauthInfo(String accessToken) {
27+
public OAuthInfoResponse requestOauthInfo(String socialToken) {
2828
String url = apiUrl + "/v1/nid/me";
2929

3030
HttpHeaders httpHeaders = new HttpHeaders();
3131
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
32-
httpHeaders.set("Authorization", "Bearer " + accessToken);
32+
httpHeaders.set("Authorization", "Bearer " + socialToken);
3333

3434
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
3535

src/main/java/com/pawwithu/connectdog/domain/oauth/api/OAuthApiClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55

66
public interface OAuthApiClient {
77
SocialType socialType();
8-
OAuthInfoResponse requestOauthInfo(String accessToken);
8+
OAuthInfoResponse requestOauthInfo(String socialToken);
99
}

src/main/java/com/pawwithu/connectdog/domain/oauth/controller/OAuthController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ public class OAuthController {
2626
@Operation(summary = "이동봉사자 소셜 로그인", description = "이동봉사자 소셜 로그인을 합니다.",
2727
responses = {@ApiResponse(responseCode = "204", description = "이동봉사자 소셜 로그인 성공")
2828
, @ApiResponse(responseCode = "400"
29-
, description = "V1, AccessToken은 필수 입력 값입니다. \t\n V1, provider는 필수 입력 값입니다. \t\n M1, 해당 이동봉사자를 찾을 수 없습니다. \t\n A5, provider 값이 KAKAO 또는 NAVER가 아닙니다."
29+
, description = "V1, AccessToken/IdToken은 필수 입력 값입니다. \t\n V1, provider는 필수 입력 값입니다. \t\n M1, 해당 이동봉사자를 찾을 수 없습니다. \t\n A5, provider 값이 KAKAO/NAVER/APPLE 중에 없습니다." +
30+
" \t\n A10, 애플 공개 키를 찾을 수 없습니다. \t\n A11, 애플 id_token 검증에 실패하였습니다. \t\n A12, 애플에서 발급된 토큰이 아닙니다. \t\n A13 코넥독 앱에서 발급된 토큰이 아닙니다."
3031
, content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
3132
})
3233
@PostMapping("/volunteers/login/social")

src/main/java/com/pawwithu/connectdog/domain/oauth/dto/request/SocialLoginRequest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
import jakarta.validation.constraints.NotNull;
66

77
public record SocialLoginRequest(
8-
@NotBlank(message = "AccessToken은 필수 입력 값입니다.")
9-
String accessToken,
8+
@NotBlank(message = "AccessToken/IdToken은 필수 입력 값입니다.")
9+
String socialToken,
1010
@NotNull(message = "provider는 필수 입력 값입니다.")
1111
SocialType provider) {
1212
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.pawwithu.connectdog.domain.oauth.dto.response;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import com.pawwithu.connectdog.domain.volunteer.entity.SocialType;
6+
import lombok.AllArgsConstructor;
7+
import lombok.Getter;
8+
9+
@Getter
10+
@JsonIgnoreProperties(ignoreUnknown = true)
11+
@AllArgsConstructor
12+
public class AppleInfoResponse implements OAuthInfoResponse {
13+
@JsonProperty("id")
14+
private String id; // 애플이 제공하는 사용자 고유 ID (sub)
15+
16+
@Override
17+
public SocialType getSocialType() {
18+
return SocialType.APPLE;
19+
}
20+
@Override
21+
public String getId() {
22+
return id;
23+
}
24+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.pawwithu.connectdog.domain.oauth.dto.response;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
import lombok.NoArgsConstructor;
6+
import lombok.Setter;
7+
8+
import java.util.List;
9+
10+
@Getter
11+
@Setter
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
public class OidcPublicKeyListResponse {
15+
private List<OidcPublicKeyResponse> keys;
16+
}

0 commit comments

Comments
 (0)