728x90
목적
: 이 페이지는 구글로그인 구현시 타임리프 없이 구글 로그인 통신 및 로그인 플로우가 정상적으로 돌아가는 지 확인하기 위함.
* 프론트와 맞춰서 하는 경우의 Spring 코드는 아래 2번 글 참고
[Oauth2.0] Google 로그인 (프론트엔드 브라우저 연동 버젼) : https://radpro.tistory.com/534
사전준비 (프론트 연동버젼 참고)
1. Google API 콘솔에서 OAuth2 설정 : 구글 OAuth2 링크2. Google API 콘솔에서 클라이언트ID와 Secret 생성3. Google API 콘솔에서 요청시 인증코드 및 액세스 토큰발급, 유저정보 받을 리다이렉트 URI 설정
구현 코드
1. GoogleLoginReqVo
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class GoogleLoginReqVo {
private String clientId; // 애플리케이션의 클라이언트 ID
private String redirectUri; // Google 로그인 후 redirect 위치
private String clientSecret; // 클라이언트 보안 비밀
private String responseType; // Google OAuth 2.0 엔드포인트가 인증 코드를 반환하는지 여부
private String scope; // OAuth 동의범위
private String code;
private String accessType; // 사용자가 브라우저에 없을 때 애플리케이션이 액세스 토큰을 새로 고칠 수 있는지 여부
private String grantType;
private String state;
private String includeGrantedScopes; // 애플리케이션이 컨텍스트에서 추가 범위에 대한 액세스를 요청하기 위해 추가 권한 부여를 사용
private String loginHint; // 애플리케이션이 인증하려는 사용자를 알고 있는 경우 이 매개변수를 사용하여 Google 인증 서버에 힌트를 제공
private String prompt; // default: 처음으로 액세스를 요청할 때만 사용자에게 메시지가 표시
}
2. GoogleLoginResVo
@Data
@NoArgsConstructor
public class GoogleLoginResVo {
private String accessToken;
private String expiresIn;
private String refreshToken;
private String scope;
private String tokenType;
private String idToken;
}
3. GoogleLoginDto
@Data
@NoArgsConstructor
public class GoogleLoginDto {
private String iss;
private String azp;
private String aud;
private String sub;
private String email;
private String emailVerified;
private String atHash;
private String name;
private String picture;
private String givenName;
private String familyName;
private String locale;
private String iat;
private String exp;
private String alg;
private String kid;
private String typ;
}
4. GoogleOauthController
package TeamBigDipper.UYouBooDan.global.oauth2.google;
import TeamBigDipper.UYouBooDan.global.security.jwt.JwtTokenizer;
import TeamBigDipper.UYouBooDan.member.entity.Member;
import TeamBigDipper.UYouBooDan.member.service.MemberService;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import javax.servlet.http.HttpServletResponse;
@RequiredArgsConstructor
@RestController
@RequestMapping("/google")
public class GoogleOauthController {
@Getter
@Value("${oauth.google.clientId}")
private String googleClientId;
@Getter
@Value("${oauth.google.clientSecret}")
private String googleClientSecret;
@Value("${oauth.google.scope}")
private String scopes;
private final MemberService memberService;
private final JwtTokenizer jwtTokenizer;
@GetMapping("/oauth")
public ResponseEntity<?> googleConnect() {
StringBuffer url = new StringBuffer();
url.append("https://accounts.google.com/o/oauth2/v2/auth?");
url.append("client_id=" + getGoogleClientId());
url.append("&redirect_uri=http://localhost:8080/google/login/redirect");
url.append("&response_type=code");
url.append("&scope="+ getScopeUrl());
return new ResponseEntity<>(url.toString(), HttpStatus.OK);
}
@GetMapping("/login/redirect")
public String redirectGoogleLogin(@RequestParam("code") String code, HttpServletResponse response) throws JsonProcessingException {
// Http 통신을 위한 RestTemplate 활용
RestTemplate restTemplate = new RestTemplate();
GoogleLoginRequest request = GoogleLoginRequest.builder()
.clientId(getGoogleClientId())
.clientSecret(getGoogleClientSecret())
.code(code)
.redirectUri("http://localhost:8080/google/login/redirect")
.grantType("authorization_code")
.build();
ResponseEntity<GoogleLoginDto> userDetailsResponse = null;
GoogleLoginDto googleProfile;
try {
// Http Header 설정
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<GoogleLoginRequest> httpReqEntity = new HttpEntity<>(request, headers);
ResponseEntity<String> apiResJson = restTemplate.postForEntity("https://oauth2.googleapis.com" + "/token", httpReqEntity, String.class);
// ObjectMapper를 통해 String to Object로 변환
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // NULL이 아닌 값만 응답 받기
GoogleLoginResponse loginResponse = mapper.readValue(apiResJson.getBody(), new TypeReference<>() {});
// 사용자의 정보는 JWT Token으로 저장되어 있고, Id_Token에 값을 저장
String jwtToken = loginResponse.getIdToken();
// JWT Token을 전달해 JWT에 저장된 사용자 정보 확인
String requestUrl = UriComponentsBuilder.fromHttpUrl("https://oauth2.googleapis.com/tokeninfo").queryParam("id_token", jwtToken).toUriString();
String resultJson = restTemplate.getForObject(requestUrl, String.class);
if(resultJson != null) {
GoogleLoginDto loginDto = mapper.readValue(resultJson, new TypeReference<>() {});
userDetailsResponse = ResponseEntity.ok().body(loginDto);
} else {
throw new Exception("Google OAuth failed");
}
} catch (Exception e) {
e.printStackTrace();
}
googleProfile = userDetailsResponse.getBody();
Member googleMember = memberService.createGoogleMember(googleProfile);
// 시큐리티 영역
// Authentication 을 Security Context Holder 에 저장
Authentication authentication = new UsernamePasswordAuthenticationToken(googleMember.getEmail(), googleMember.getPassword()); // password 확인
SecurityContextHolder.getContext().setAuthentication(authentication);
// 자체 JWT 생성 및 HttpServletResponse 의 Header 에 저장 (클라이언트 응답용)
String accessToken = jwtTokenizer.delegateAccessToken(googleMember);
String refreshToken = jwtTokenizer.delegateRefreshToken(googleMember);
response.setHeader("Authorization", "Bearer " + accessToken);
response.setHeader("RefreshToken", refreshToken);
return "Success Login: User";
}
public String getScopeUrl() {
return scopes.replaceAll(",", "%20");
}
}
728x90
'Java & Spring > Spring' 카테고리의 다른 글
[OAuth2.0] 카카오 로그아웃 (0) | 2023.02.09 |
---|---|
[OAuth2.0] Google 로그인 (프론트엔드 브라우저 연동 버젼) (0) | 2023.02.06 |
[OAuth2.0] 이론 (0) | 2023.02.06 |
[OAuth2.0] 카카오 로그인 (프론트엔드 브라우저 연동 버젼) (0) | 2023.02.03 |
[Spring] 비밀번호 검증 및 수정 로직 (0) | 2023.01.31 |