프로젝트의 OAuth2.0 구조
저희 프로젝트의 경우 소셜 로그인을 Rest api 방식이 아닌 Spring boot 에서 OAuth2.0 프로토콜을 이용하여 구현하였습니다. 때문에 로그인 할 수 있는 특정 url 이 삽입된 버튼을 클릭하면 특정 소셜 로그인 화면이 나오고, 소셜 로그인에 성공하면 해당 유저의 정보로 회원가입 / 로그인을 동시에 한 뒤 유저 정보를 반환하게 됩니다.
이렇게 하면 뭐가 문제인가요?
이런식으로 리다이렉트 됩니다. 프론트에서 아래와 같이 잡아도
<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';
import router from "../../router/index.js";
const responseData = ref(null);
onMounted(async () => {
try {
const response = await axios.post('https://dev.travel-planner.xyz/oauth/callback');
responseData.value = response.data;
console.log('Response Data:', responseData.value);
if (response.status === 200) {
await router.push('/feed');
}
} catch (error) {
console.error('Error:', error);
}
});
</script>
JavaScript
복사
리다이렉트 되는 주소가 서버 주소이기 때문에 위와 같은 화면이 나올 수 밖에 없었습니다.
프론트 주소로 리다이렉트 하기
이를 해결하기 위해 아래와 같이 소셜로그인에 성공하면 유저 정보를 리퀘스트 파라미터로 유저 정보를 넣은 주소를 반환 하는 방식으로 변경하였습니다.
String encodedUserId = URLEncoder.encode(String.valueOf(authResponse.getUserId()), StandardCharsets.UTF_8);
String encodedEmail = URLEncoder.encode(authResponse.getEmail(), StandardCharsets.UTF_8);
String encodedNickname = URLEncoder.encode(authResponse.getUserNickname(), StandardCharsets.UTF_8);
String encodedProvider = URLEncoder.encode(authResponse.getProvider(), StandardCharsets.UTF_8);
String encodedProfileImgUrl = URLEncoder.encode(authResponse.getProfileImgUrl(), StandardCharsets.UTF_8);
// 프론트엔드 페이지로 토큰과 함께 리다이렉트
String frontendRedirectUrl = String.format(
"%s/oauth/callback?token=%s&userId=%s&email=%s&nickname=%s&provider=%s&profileImgUrl=%s",
frontendRedirectUri, accessToken, encodedUserId, encodedEmail, encodedNickname,
encodedProvider, encodedProfileImgUrl
);
response.sendRedirect(frontendRedirectUrl);
Java
복사
프론트의 경우 아래와 같이 변경하였습니다.
<template>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useStore } from "vuex";
const route = useRoute();
const router = useRouter();
const store = useStore();
const email = ref('');
const nickname = ref('');
const provider = ref('');
const profileImgUrl = ref('');
const token = ref('');
onMounted(() => {
email.value = route.query.email;
nickname.value = route.query.nickname;
provider.value = route.query.provider;
profileImgUrl.value = route.query.profileImgUrl;
token.value = route.query.token;
if (token.value) {
sessionStorage.setItem('accessToken', token.value);
}
const userInfo = {
email: email.value,
nickname: nickname.value,
provider: provider.value,
profileImgUrl: profileImgUrl.value,
};
if (userInfo) {
sessionStorage.setItem('userInfo', JSON.stringify(userInfo));
}
store.commit('setLoginUser', userInfo);
router.push('/feed');
});
</script>
Java
복사
이렇게 하면 알맞은 데이터를 받아와 화면 갱신이 가능합니다.
Redirect
HTTP 리디렉션은 일반적으로 클라이언트(일반적으로 웹 브라우저)에게 다른 URL에 대한 새 요청을 생성하도록 지시하는 데 사용됩니다. 리디렉션 응답이 클라이언트로 전송되면 일반적으로 응답 본문이 포함되지 않습니다.
총정리
OAuth2.0 프로토콜을 이용하여 스프링부트에서 SSO 를 구현하였습니다.
로그인 요청 보내기
먼저 GET https://dev.travel-planner.xyz/oauth/authorize/google 으로 소셜로그인 요청을 보냅니다.
로그인 하기
아이디와 비밀번호를 입력하면 아래와 같이 각종 정보와 리다이렉트 주소가 담긴url 로 로그인을 합니다.
https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=…&redirect_uri=https://dev.travel-planner.xyz/oauth/callback
로그인 요청
https://dev.travel-planner.xyz/oauth/callback?state=… 이곳으로 소셜 로그인에 필요한 정보를 쿼리 파라미터에 넣어 구글에 요청합니다.
로그인 성공시
서버에서 로그인 응답으로 받은 유저 정보를 받아 프론트 주소로 수정, 필요한 정보를 리퀘스트 파라미터로 담아 리다이렉트 합니다.
http://localhost:5173/oauth/callback?token=…
여기서의 토큰은 accesstoken 이며, 파라미터명은 마음대로 정하면 됩니다.