Skip to content

Commit e7e85d0

Browse files
Merge pull request #182 from DevKor-github/mod/googlelogin
[Mod] google 로그인 요청을 idtoken로 수정
2 parents d99b5aa + 1dfa9b3 commit e7e85d0

File tree

5 files changed

+79
-55
lines changed

5 files changed

+79
-55
lines changed

ontime-back/src/main/java/devkor/ontime_back/dto/OAuthGoogleRequestDto.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44

55
@Getter
66
public class OAuthGoogleRequestDto {
7-
private String accessToken;
7+
private String idToken;
88
private String refreshToken;
99
}
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
package devkor.ontime_back.dto;
22

3-
import com.fasterxml.jackson.annotation.JsonProperty;
43
import lombok.Getter;
5-
import lombok.Setter;
64

75
@Getter
86
public class OAuthGoogleUserDto {
97

108
private String id; // 고유 사용자 ID
119
private String name; // 사용자 이름
12-
@JsonProperty("given_name") // JSON의 given_name 필드와 매핑
13-
private String givenName;
14-
@JsonProperty("family_name") // JSON의 family_name 필드와 매핑
15-
private String familyName;
1610
private String picture; // 프로필 이미지 URL
1711
private String email; // 이메일
18-
@JsonProperty("email_verified")
19-
private boolean emailVerified; // 이메일 인증 여부
2012

13+
public OAuthGoogleUserDto(String id, String name, String picture, String email) {
14+
this.id = id;
15+
this.name = name;
16+
this.picture = picture;
17+
this.email = email;
18+
}
2119
}

ontime-back/src/main/java/devkor/ontime_back/global/oauth/apple/AppleLoginService.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,25 @@
33
import com.fasterxml.jackson.databind.JsonNode;
44
import com.fasterxml.jackson.databind.ObjectMapper;
55
import devkor.ontime_back.dto.AppleTokenResponseDto;
6-
import devkor.ontime_back.dto.OAuthAppleRequestDto;
76
import devkor.ontime_back.dto.OAuthAppleUserDto;
87
import devkor.ontime_back.entity.Role;
98
import devkor.ontime_back.entity.SocialType;
109
import devkor.ontime_back.entity.User;
10+
import devkor.ontime_back.entity.UserSetting;
1111
import devkor.ontime_back.global.jwt.JwtTokenProvider;
1212
import devkor.ontime_back.global.jwt.JwtUtils;
1313
import devkor.ontime_back.repository.UserRepository;
14+
import devkor.ontime_back.response.InvalidTokenException;
1415
import io.jsonwebtoken.Claims;
1516
import io.jsonwebtoken.Jwts;
1617
import io.jsonwebtoken.SignatureAlgorithm;
17-
import jakarta.servlet.http.HttpServletRequest;
1818
import jakarta.servlet.http.HttpServletResponse;
1919
import lombok.RequiredArgsConstructor;
2020
import lombok.extern.slf4j.Slf4j;
2121
import org.springframework.beans.factory.annotation.Value;
2222
import org.springframework.http.*;
2323
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
2424
import org.springframework.security.core.Authentication;
25-
import org.springframework.security.core.AuthenticationException;
2625
import org.springframework.security.core.authority.SimpleGrantedAuthority;
2726
import org.springframework.security.core.context.SecurityContextHolder;
2827
import org.springframework.stereotype.Service;
@@ -39,10 +38,8 @@
3938
import java.security.PrivateKey;
4039
import java.security.PublicKey;
4140
import java.security.spec.PKCS8EncodedKeySpec;
42-
import java.util.Collections;
43-
import java.util.Date;
44-
import java.util.Map;
45-
import java.util.Optional;
41+
import java.time.Instant;
42+
import java.util.*;
4643

4744
@Slf4j
4845
@RequiredArgsConstructor
@@ -114,6 +111,15 @@ public Authentication handleRegister(String appleRefreshToken, OAuthAppleUserDto
114111
.socialLoginToken(appleRefreshToken)
115112
.build();
116113

114+
UUID userSettingId = UUID.randomUUID();
115+
116+
UserSetting userSetting = UserSetting.builder()
117+
.userSettingId(userSettingId)
118+
.user(newUser)
119+
.build();
120+
121+
newUser.setUserSetting(userSetting);
122+
117123
User savedUser = userRepository.save(newUser);
118124

119125
String accessToken = jwtTokenProvider.createAccessToken(newUser.getEmail(), newUser.getId());
@@ -151,11 +157,16 @@ public Claims verifyIdentityToken(String identityToken) throws
151157
Claims tokenClaims = jwtUtils.getTokenClaims(identityToken, publicKey);
152158
// iss 확인
153159
if (!issuer.equals(tokenClaims.getIssuer())) {
154-
throw new IllegalArgumentException("Invalid JWT: Issuer mismatch. Expected: " + issuer);
160+
throw new InvalidTokenException("유효하지 않은 JWT입니다. issuer가 일치하지 않습니다.");
155161
}
156162
// aud 확인
157163
if (!clientId.equals(tokenClaims.getAudience())) {
158-
throw new IllegalArgumentException("Invalid JWT: Audience mismatch. Expected: " + clientId);
164+
throw new InvalidTokenException("유효하지 않은 JWT입니다. audience가 일치하지 않습니다.");
165+
}
166+
// exp(만료 시간) 확인
167+
Date expiration = tokenClaims.getExpiration();
168+
if (expiration == null || expiration.before(Date.from(Instant.now()))) {
169+
throw new InvalidTokenException("유효하지 않은 JWT입니다. 만료되었습니다.");
159170
}
160171

161172
return tokenClaims;

ontime-back/src/main/java/devkor/ontime_back/global/oauth/google/GoogleLoginFilter.java

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,23 @@
11
package devkor.ontime_back.global.oauth.google;
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
45
import devkor.ontime_back.dto.OAuthGoogleRequestDto;
56
import devkor.ontime_back.dto.OAuthGoogleUserDto;
6-
import devkor.ontime_back.entity.Role;
77
import devkor.ontime_back.entity.SocialType;
88
import devkor.ontime_back.entity.User;
9-
import devkor.ontime_back.global.jwt.JwtTokenProvider;
109
import devkor.ontime_back.repository.UserRepository;
1110
import jakarta.servlet.FilterChain;
1211
import jakarta.servlet.ServletException;
1312
import jakarta.servlet.http.HttpServletRequest;
1413
import jakarta.servlet.http.HttpServletResponse;
1514
import lombok.extern.slf4j.Slf4j;
16-
import org.springframework.http.HttpEntity;
17-
import org.springframework.http.HttpHeaders;
18-
import org.springframework.http.ResponseEntity;
19-
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
2015
import org.springframework.security.core.Authentication;
2116
import org.springframework.security.core.AuthenticationException;
22-
import org.springframework.security.core.authority.SimpleGrantedAuthority;
2317
import org.springframework.security.core.context.SecurityContextHolder;
2418
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
25-
import org.springframework.web.client.RestTemplate;
2619

2720
import java.io.IOException;
28-
import java.util.Collections;
2921
import java.util.Optional;
3022

3123
@Slf4j
@@ -45,21 +37,27 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ
4537
throws AuthenticationException, IOException, ServletException {
4638
ObjectMapper objectMapper = new ObjectMapper();
4739
OAuthGoogleRequestDto oAuthGoogleRequestDto = objectMapper.readValue(request.getInputStream(), OAuthGoogleRequestDto.class);
48-
OAuthGoogleUserDto oAuthGoogleUserInfo = googleLoginService.getUserInfoFromAccessToken(oAuthGoogleRequestDto.getAccessToken());
4940

50-
Optional<User> existingUser = userRepository.findBySocialTypeAndSocialId(SocialType.GOOGLE, oAuthGoogleUserInfo.getId());
41+
try {
42+
GoogleIdToken.Payload googlePayload = googleLoginService.verifyIdentityToken(oAuthGoogleRequestDto.getIdToken());
43+
String googleUserId = googlePayload.getSubject();
5144

45+
Optional<User> existingUser = userRepository.findBySocialTypeAndSocialId(SocialType.GOOGLE, googleUserId);
5246

53-
if (existingUser.isPresent()) {
54-
return googleLoginService.handleLogin(oAuthGoogleRequestDto, existingUser.get(), response);
55-
} else {
56-
return googleLoginService.handleRegister(oAuthGoogleRequestDto, oAuthGoogleUserInfo, response);
57-
}
58-
}
59-
47+
if (existingUser.isPresent()) {
48+
return googleLoginService.handleLogin(oAuthGoogleRequestDto, existingUser.get(), response);
49+
} else {
50+
OAuthGoogleUserDto oAuthGoogleUserDto = new OAuthGoogleUserDto(googleUserId, (String) googlePayload.get("name"), (String) googlePayload.get("picture"), googlePayload.getEmail());
51+
return googleLoginService.handleRegister(oAuthGoogleRequestDto, oAuthGoogleUserDto, response);
52+
}
6053

54+
} catch (Exception e) {
55+
log.error("Google 로그인 실패: {}", e.getMessage(), e);
56+
throw new AuthenticationException("Google 로그인 실패") {};
57+
}
6158

6259

60+
}
6361

6462
// 인증 성공 처리
6563
@Override

ontime-back/src/main/java/devkor/ontime_back/global/oauth/google/GoogleLoginService.java

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
package devkor.ontime_back.global.oauth.google;
2-
2+
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
3+
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
4+
import com.google.api.client.http.javanet.NetHttpTransport;
5+
import com.google.api.client.json.gson.GsonFactory;
36
import devkor.ontime_back.dto.OAuthGoogleRequestDto;
47
import devkor.ontime_back.dto.OAuthGoogleUserDto;
58
import devkor.ontime_back.entity.Role;
69
import devkor.ontime_back.entity.SocialType;
710
import devkor.ontime_back.entity.User;
11+
import devkor.ontime_back.entity.UserSetting;
812
import devkor.ontime_back.global.jwt.JwtTokenProvider;
913
import devkor.ontime_back.repository.UserRepository;
14+
import io.jsonwebtoken.Claims;
1015
import jakarta.servlet.http.HttpServletResponse;
1116
import lombok.RequiredArgsConstructor;
1217
import lombok.extern.slf4j.Slf4j;
18+
import org.springframework.beans.factory.annotation.Value;
1319
import org.springframework.http.HttpEntity;
1420
import org.springframework.http.HttpHeaders;
1521
import org.springframework.http.HttpMethod;
@@ -24,6 +30,7 @@
2430
import java.io.IOException;
2531
import java.util.Collections;
2632
import java.util.Optional;
33+
import java.util.UUID;
2734

2835
@Slf4j
2936
@Service
@@ -35,25 +42,9 @@ public class GoogleLoginService {
3542
private static final String GOOGLE_USER_INFO_URL = "https://www.googleapis.com/userinfo/v2/me";
3643
private static final String GOOGLE_REVOKE_URL = "https://oauth2.googleapis.com/revoke?token=";
3744

45+
@Value("${spring.security.oauth2.client.registration.google.client-id}")
46+
private String clientId;
3847

39-
40-
public OAuthGoogleUserDto getUserInfoFromAccessToken(String accessToken) {
41-
RestTemplate restTemplate = new RestTemplate();
42-
43-
HttpHeaders headers = new HttpHeaders();
44-
headers.set("Authorization", "Bearer " + accessToken);
45-
46-
HttpEntity<String> entity = new HttpEntity<>(headers);
47-
48-
ResponseEntity<OAuthGoogleUserDto> response = restTemplate.exchange(
49-
GOOGLE_USER_INFO_URL,
50-
org.springframework.http.HttpMethod.GET,
51-
entity,
52-
OAuthGoogleUserDto.class
53-
);
54-
55-
return response.getBody();
56-
}
5748
public Authentication handleLogin(OAuthGoogleRequestDto oAuthGoogleRequestDto, User user, HttpServletResponse response) throws IOException {
5849
user.updateSocialLoginToken(oAuthGoogleRequestDto.getRefreshToken());
5950

@@ -98,6 +89,15 @@ public Authentication handleRegister(OAuthGoogleRequestDto oAuthGoogleRequestDto
9889
.socialLoginToken(oAuthGoogleRequestDto.getRefreshToken())
9990
.build();
10091

92+
UUID userSettingId = UUID.randomUUID();
93+
94+
UserSetting userSetting = UserSetting.builder()
95+
.userSettingId(userSettingId)
96+
.user(newUser)
97+
.build();
98+
99+
newUser.setUserSetting(userSetting);
100+
101101
User savedUser = userRepository.save(newUser);
102102

103103
String accessToken = jwtTokenProvider.createAccessToken(newUser.getEmail(), newUser.getId());
@@ -123,6 +123,23 @@ public Authentication handleRegister(OAuthGoogleRequestDto oAuthGoogleRequestDto
123123
return authentication;
124124
}
125125

126+
public GoogleIdToken.Payload verifyIdentityToken(String identityToken) throws Exception {
127+
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(
128+
new NetHttpTransport(),
129+
GsonFactory.getDefaultInstance())
130+
.setAudience(Collections.singletonList(clientId)) // aud 확인
131+
.build();
132+
133+
GoogleIdToken idToken = verifier.verify(identityToken); // Google의 공개 키를 사용하여 idToken 서명을 검증
134+
if (idToken != null) {
135+
GoogleIdToken.Payload payload = idToken.getPayload();
136+
return payload;
137+
} else {
138+
log.info("유효하지 않은 idtoken 입니다.");
139+
return null;
140+
}
141+
}
142+
126143
public boolean revokeToken(Long userId) {
127144
User user = userRepository.findById(userId)
128145
.orElseThrow(() -> new IllegalArgumentException("User not found with id: " + userId));

0 commit comments

Comments
 (0)