본문 바로가기

JAVA/Spring

[JAVA] Vue3 + Spring boot : vue3-google-login 라이브러리로 Google 로그인 기능 만들기

 

 

 

 

Vue3, Java + Spring boot

vue3-google-login 라이브러리를 이용한 구글 로그인 기능을 만들었다.

 

각자 등록해둔 설정 값에 따라 url 사용방식은 바뀌겠지만 우선은 글대로 따라하면 될 것 같다.

로그인 token 값과 user 정보를 같이 넘겨받는 로직이다.

 

Vue3 - Front

<GoogleLogin
    :callback="handleGoogleLoginSuccess"
    @error="handleError"
    class="btn-round-line"
/>


import { GoogleLogin } from 'vue3-google-login'

 

vue3-google-login 은 로그인 버튼을 기본적으로 지원해주고 라이브러리 자체가 Oauth2 작업을 처리해준다.

컴포넌트만 쓰면 되서 아주 간편하다.

 

  • 구글 로그인 API 호출
// 토큰 값 디코딩
const parseJwt = (token) => {
  const base64Url = token.split('.')[1]
  const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
  const jsonPayload = decodeURIComponent(
    atob(base64)
      .split('')
      .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
      .join('')
  )
  return JSON.parse(jsonPayload)
}

// 구글 로그인
const handleGoogleLoginSuccess = async (googleUser) => {
  console.log('Google 로그인 성공:', googleUser)

  const decoded = parseJwt(googleUser.credential)
  console.log('디코딩 정보:', decoded)

  const providerId = decoded.sub
  const email = decoded.email
  const name = decoded.name

    $axios
        .post(
            $apiUrls.auth.userGoogleLogin,
            {
            idToken: googleUser.credential,
            provider: "google",
            providerId: providerId,
            email,
            name,
            },
            {
            headers: {
                'Content-Type': 'application/json'
            }
        }
    )
    .then((res) => {
        console.log("Google 로그인 성공", res.data)
        const token = res.data.token;
        const user = res.data.user;

        localStorage.setItem('token', token) // local 저장 - 쿠키사용
        localStorage.setItem('user', JSON.stringify(user)) // local 저장
        setAuth(token, user) // 내가 auth.js 만들어 사용하는 것

        const redirectPath = route.query.redirectTo || '/'
        router.push(redirectPath)
    })
    .catch((err) => {
        console.error("Google 로그인 실패", err)
    })
};

 

vue3-google-login 라이브러리 팝업을 통해 로그인을 하면 바로 토큰 값이 넘어온다.

토큰 값을 디코딩 후 정보를 Json 형태로 서버에 전달한다.

 

전달 후 token , user 정보를 localStoriage에 세팅한다. -> 쿠키에 저장되서 사용

 

JAVA + Spring boot - Backend

  • Controller
    @PostMapping("/login/google") 
    public ResponseEntity<?> googleLogin(@RequestBody Map<String, String> body) {
    	String idToken = body.get("idToken");
	    if (idToken == null || idToken.isEmpty()) {
	        return ResponseEntity.badRequest().body(Map.of("message", "idToken is required"));
	    }
	    Map<String, Object> loginResult = authService.googleLogin(body);
	    return ResponseEntity.status(HttpStatus.OK).body(loginResult);
    }

 

클라이언트에서 전달 받은 token 값을 통해 데이터 가공 후 클라이언트로 해당 정보를 다시 전달한다.

Oauth 2 를 통한 구글 로그인과 다르게 body 에 담아 보내기만 하면 되서 훨씬 간편하다.

 

원래는 getMapping을 하지만 나는 user정보도 포함해서 넘겨주기 위해 postMapping을 사용했다.

 

  • Service
    public Map<String, Object> googleLogin(Map<String, String> body) {
    	String idToken = body.get("idToken");
        // 구글 토큰 검증
        GoogleUserEntity googleUser = googleTokenVerifier.verify(idToken);
        if (googleUser == null) {
            throw new RuntimeException("Invalid Google token");
        }
        // 유저 생성 - 중복 체크
        Optional<UserEntity> userOptional = userRepository.findByProviderAndProviderId(
                googleUser.getProvider(),
                googleUser.getProviderId()
        );

        UserEntity user;

        if (userOptional.isPresent()) {
            user = userOptional.get();
            // 가입된 사용자일 경우 마지막 로그인 일시 업데이트 추가
            userRepository.updateLastLoginDt(user.getUserId(), LocalDateTime.now());
        } else {
            // 이메일 중복 확인
            boolean emailExists = userRepository.existsByEmail(googleUser.getEmail());
            if (emailExists) {
                throw new RuntimeException("이미 가입된 이메일입니다.");
            }
            // 중복 아닐 경우 새로운 사용자 생성
            String userId = uniqueKeyGenerator.getUniqueKey(null);
            UserEntity newUser = UserEntity.builder()
            		.userId(userId)
                    .provider(googleUser.getProvider())
                    .providerId(googleUser.getProviderId())
                    .email(googleUser.getEmail())
                    .userNm(googleUser.getName())
                    .createdAt(LocalDateTime.now())
                    .withdrawalDt(null)
                    .build();
            user = userRepository.save(newUser);
            
            //사용자 생성 후 가입메일 발송
            boolean mailResult = mailComponent.sendMailBySingUp(user.getEmail(), user.getUserNm());
            if(mailResult) {
            	userRepository.updateMailSendYn(userId, "Y");
            }
        }
        // JWT 토큰 생성
        String jwtToken = jwtTokenProvider.generateToken(googleUser);

        return Map.of(
            "token", jwtToken,
            "user", user
        );
    }

 

 

Token 값 검증

-> 유저생성 -> 중복 체크

-> JWT 토큰 생성

-> 클라이언트 전달

 

  • GoogleTokenVerifier
@Component
public class GoogleTokenVerifier {

    private final GoogleIdTokenVerifier verifier;

    private final String clientId = ""; // 구글 인증 클라이언트 ID
    
    public GoogleTokenVerifier() {
    	verifier = new GoogleIdTokenVerifier.Builder(
    	        new NetHttpTransport(),
    	        GsonFactory.getDefaultInstance()
    	    )
    	    .setAudience(Collections.singletonList(clientId))
    	    .build();
    }
    
    public GoogleUserEntity verify(String idTokenString) {
        try {
            GoogleIdToken idToken = verifier.verify(idTokenString);
            if (idToken != null) {
                GoogleIdToken.Payload payload = idToken.getPayload();

                String provider = "google";
                String providerId = payload.getSubject();
                String email = payload.getEmail();
                String name = (String) payload.get("name");

                return new GoogleUserEntity(provider, providerId, email, name);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

 

구글 클라이언트 ID를 통해 verifier 를 세팅해준다.

Service 에서 넘겨 받은 tokenString 값으로 해당 토큰값이 구글에 등록 된 값과 일치하는지 비교 후 정보를 담아 return 한다.

GoogleUserEntity 는 google Login 정보를 담을 Entity.

 

  • JwtTokenProvider
@Component
public class JwtTokenProvider {

	// JWT 서명 키 - 문자열 복호화해서 시크릿키로 변환
	private final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(
		java.util.Base64.getDecoder().decode("Base64 인코딩한 문자열")
	);
	
    // JWT 만료 시간 설정
    private final long EXPIRATION = 1000L * 60 * 60 * 24;
    
    // JWT 에 담길 정보
    public String generateToken(GoogleUserEntity user) {
        return Jwts.builder()
                .setSubject(user.getEmail())
//                .claim("userId", user.getUserId())
                .claim("name", user.getName())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
                .signWith(SECRET_KEY, SignatureAlgorithm.HS256)
                .compact();
    }
}

 

 

 

 

상세한 설정과 보안이 중요하다면 라이브러리가 아닌 Oauth 2.0 + google play console 을 이용하는게 좋다.

 

 

[JAVA] Vue3 + Spring boot : Oauth2.0 Google 로그인 기능 만들기

Vue3, Java + Spring bootOauth 2.0, Google play console 을 이용한 구글 로그인 기능을 만들었다. 각자 등록해둔 설정 값에 따라 url 사용방식은 바뀌겠지만 우선은 글대로 따라하면 될 것 같다. - Oauth 2.0, Google

devel-log.tistory.com