📃 요약
Multi-provider social login 샘플입니다.
구글 로그인 , 네이버 로그인을 이용해서 진행해 보도록 하겠습니다.
구글 클라우드에 API 신청 및 clientId, clientSecret 로 springboot 에 설정합니다.
마찬가지로, 네이버 디벨로퍼 사이트에서 API 신청 및 clientId, clientSecret 로 springboot 에 설정합니다.
소셜 로그인할 때 DB 에 소셜로그인 사용자를 저장해 보겠습니다.
다중 공급자일때는 어떻게 구현하는지를 보시면 되겠습니다.
글에 언급되지 않는 일반 로그인 소스가(Spring Security) 포함되어 있습니다.
구글 / 네이버 로그인 등록은 생략하겠습니다.
구글 / 네이버 로그인 등록에 관한 것은 아래 주소를 참고하세요
( 참조 블로그 : https://deeplify.dev/back-end/spring/oauth2-social-login )
프론트는 Vue 를 사용하고, 벡엔드는 spring boot 를 사용합니다.
DB 는 오라클 도커 이미지를 사용하고 계정은 scott ( 암호 : !Ds1234567890 ) 개발자 계정을 생성하고 사용합니다.
DB 개발자 계정 및 설치하는 방법은 생략합니다.
요소 기술 :
– 프론트엔드 : Vue
– 벡엔드 : Spring Boot & JPA
– DB : Oracle 18xe(Docker)
( Oracle 18xe 도커 이미지 주소 : https://hub.docker.com/r/kangtaegyung/oraclexe-18c )
결과 화면 :
- 로그인 화면
- 구글 계정 선택 화면 #1
- 구글 계정 선택 화면 #2
- Home 화면 이동
- 구글 로그인 완료 후 새로운 구글 사용자 테이블에 저장됨
프로젝트 탐색기 : Vue
프로젝트 탐색기 : String Boot
Front : 소셜 로그인 url 정보
메소드 | URL | 설명 |
---|---|---|
GET | http://localhost:8000/oauth2/code/google | LoginView.vue 구글 로그인 버튼 클릭시 실행될 주소 구글 클라우드에서 등록된 승인된 리다이렉트 URL 주소 |
GET | /auth-redirect | SocialRedirectView.vue 벡엔드에서 보내준 웹토큰, 유저정보를 로컬스토리지에 저장하고 동시에 Home 으로 강제 이동함 |
Backend : 소셜 로그인 URL 정보
메소드 | URL | 설명 |
---|---|---|
GET | http://localhost:8080/auth-redirect | SocialLoginSuccess.java 소셜로그인 성공후 Vue 리다이렉트 주소로 웹토큰, 유저정보 전송할 URL 정보 |
소셜 로그인 간단 절차
- 1) 로그인 버튼 클릭 : 소셜 로그인 사이트에서 제공된 URL 입니다.
- 구글 : http://벡엔드주소/oauth2/authorization/google
- 네이버 : http://벡엔드주소/oauth2/authorization/naver
- 2) 로그인 자동 인증 : 스프링 OAuth 패키지가 소셜 로그인 공급자에게 받은 인가코드로 인증 토큰을 자동적으로 받아옵니다.
- 3) 인증 토큰을 받은 후 추가 로직을 개발자가 구현합니다.
- DB 에 새 사용자 저장
- Vue 로 웹토큰(JWT) , 사용자 부가 정보 전송
📃 기술 구현
스펙 :
테이블 설계
DROP TABLE TB_MEMBER CASCADE CONSTRAINTS; CREATE TABLE TB_MEMBER ( EMAIL VARCHAR2(1000) NOT NULL PRIMARY KEY, -- id (email) PASSWORD VARCHAR2(1000), -- 암호 NAME VARCHAR2(1000), -- 유저명 CODE_NAME VARCHAR2(1000), -- 권한코드명(ROLE_USER, ROLE_ADMIN) DELETE_YN VARCHAR2(1) DEFAULT 'N', INSERT_TIME VARCHAR2(255), UPDATE_TIME VARCHAR2(255), DELETE_TIME VARCHAR2(255) );
소셜 로그인 구현을 위한 테이블 설계입니다.
데이터베이스를 오라클(Docker)을 사용하여 구현해 보겠습니다.
Spring build.gradle : dependencies 블럭 내 추가
dependencies { // OAUTH2 라이브러리 추가 : 소셜 로그인 등 클라이언트 입장에서 소셜 기능 구현 시 필요한 의존성 implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' ... 생략 }
Spring Security 설정 클래스
-WebSecurityConfig.java
package org.example.simpledms.config; import jakarta.servlet.DispatcherType; import lombok.RequiredArgsConstructor; import org.example.simpledms.security.oauth.SocialLoginSuccess; import org.example.simpledms.security.oauth.SocialLoginServiceCustom; import org.example.simpledms.security.jwt.AuthTokenFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * @fileName : WebSecurityConfig * @author : GGG * @since : 2024-04-15 * description : * 1) DB 인증을 위한 함수 : passwordEncoder() * 2) 패스워드 암호화 함수 : 필수 정의 * @Bean : IOC (스프링이 객체를 생성해주는 것), 함수의 리턴객체를 생성함 * => (참고) 용어 : 스프링 생성한 객체 == 빈(Bean==콩) * 3) JWT 웹토큰 자동인증 함수 : authenticationJwtTokenFilter() * 4) img, css, js 등 인증 무시 설정 함수 : webSecurityCustomizer() * => 사용법 : (web) -> web.ignoring().requestMatchers("경로", "경로2"...) * 5) 스프링 시큐리티 규칙 정의 함수(***) : filterChain(HttpSecurity http) * 5-1) cors 사용 * 5-2) csrf 해킹 보안 비활성화(쿠키/세션 사용않함) * 5-3) 쿠키/세션 안함(비활성화) -> 로컬스토리지/웹토큰 * 5-4) form 태그 action 을 이용한 로그인 사용않함 -> axios 통신함 * 5-5) /api/auth/** : 이 url 은 모든 사용자 접근 허용, ** (하위 url 모두 포함) * 5-8) / : 이 url 은 모든 사용자 접근 허용 * 5-9) TODO : 웹토큰 클래스를 스프링시큐리티 설정에 끼워넣기 : 모든 게시판 조회(CRUD)에서 아래 인증을 실행함 * * 6) 소셜 로그인 * 6-1) 소셜 로그인 성공후 처리할 리다이렉션해서 구글 인가코드 받음 * 6-2) 구글 인가코드 확인 후에 DB 인증, 웹토큰 발행, 프론트로 전송 */ @Configuration @RequiredArgsConstructor public class WebSecurityConfig { private final SocialLoginSuccess socialLoginSuccess; private final SocialLoginServiceCustom socialLoginServiceCustom ; private final AuthTokenFilter authTokenFilter; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } // img, css, js 등 인증 무시 설정 함수 @Bean public WebSecurityCustomizer webSecurityCustomizer() { return (web) -> web.ignoring().requestMatchers( "/img/**", "/css/**", "/js/**" ); } // TODO: 스프링 시큐리티 규칙 정의 함수(***) @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.cors(Customizer.withDefaults()); // 5-1) http.csrf((csrf) -> csrf.disable()); // 5-2) http.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // 5-3) http.formLogin(req -> req.disable()); // 5-4) http.authorizeHttpRequests(req -> req .dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll() .requestMatchers("/api/auth/**").permitAll() // 5-5) .requestMatchers("/").permitAll() // 5-8) .anyRequest() .authenticated()); // TODO: 소셜 로그인 설정 부분 http.oauth2Login(req -> req .successHandler(socialLoginSuccess) // 6-1) .userInfoEndpoint(arg -> arg.userService(socialLoginServiceCustom)) // 6-2) ); // 5-9) http.addFilterBefore(authTokenFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } }
- TODO: 소셜 로그인 설정 부분이 Spring Security 에서 설정할 부분입니다.
- socialLoginSuccess : 성공후에 실행될 클래스이고, Vue 로 웹토큰 , 유저정보를 전송합니다.
- socialLoginServiceCustom : 구글, 네이버 인증토큰을 받으면 실행될 클래스 이고, DB 에 새로운 소셜 사용자를 등록합니다.
소셜 로그인 클래스 : 구글 API 인가토큰을 받아 인증 처리 하는 클래스
-SocialLoginServiceCustom.java
package org.example.simpledms.security.oauth; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.example.simpledms.model.entity.auth.Member; import org.example.simpledms.repository.auth.MemberRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Service; import java.util.Collections; /** * @fileName : SocialLoginServiceCustom * @author : kangtaegyung * @since : 2022/12/16 * description : * 알고리즘 * 1) OAuth2UserService : 유저정보 있는 클래스 * 2) registrationId : google, naver, kakao 같은 이름이 있음, 이것으로 각 서비스를 구분함 * 3) OAuth2 로그인 진행시 키가 되는 필드값, 인증토큰(PK 와 같음) * 4) registrationId 에 따라 구글함수, 네이버함수, 카카오함수를 실행하는 함수 * 5) 소셜 기본정보 DB 저장, 유저가 있으면 무시 * 6) 소셜유저 생성 및 내보내기 */ @Slf4j @Service @RequiredArgsConstructor public class SocialLoginServiceCustom implements OAuth2UserService<OAuth2UserRequest, OAuth2User> { private final MemberRepository memberRepository; @Override public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { OAuth2UserService<OAuth2UserRequest, OAuth2User> socialLoginService = new DefaultOAuth2UserService(); // 1) OAuth2User socialLogin = socialLoginService.loadUser(userRequest); String registrationId = userRequest.getClientRegistration().getRegistrationId(); // 2) String socialKey = userRequest.getClientRegistration().getProviderDetails() .getUserInfoEndpoint().getUserNameAttributeName(); // 3) SocialProviders socialProviders = SocialProviders.of(registrationId, socialKey, socialLogin.getAttributes()); // 4) saveSocialIdOrSkip(socialProviders); // 5) return new DefaultOAuth2User( // 6) Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")), socialProviders.getSocialUser(), socialProviders.getSocialKey()); } private void saveSocialIdOrSkip(SocialProviders socialProviders) { try { if(memberRepository.existsById(socialProviders.getEmail()) == false) { memberRepository.save(socialProviders.getDefaultUser()); } } catch (Exception e) { log.debug("saveSocialIdOrSkip 에러" ,e.getMessage()); } } }
- 구글 / 네이버 로그인 성공 후 인증토큰을 받으면(자동) 벡엔드에서 새로운 사용자로 DB 에 저장합니다.
- 구글, 네이버 인가코드를 통한 인증토큰은 OAuth 스프링 라이브러리가 자동적으로 처리하고 인증토큰을 받아옵니다
- 개발자는 구글 / 네이버 로그인이 정상적으로 진행되었다는 가정하에 (인증토큰 받음) 추가 로직을 작성하면 됩니다. 여기서는 DB 에 사용자를 저장합니다.
- DB 에 저장시 기본정보로 저장합니다.
– SocialLoginSuccess.java
DB 에 사용자 저장 후에 실행될 클래스 : 웹토큰 발급 및 프론트로 유저 정보 전송
package org.example.simpledms.security.oauth; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.example.simpledms.security.jwt.JwtUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; import org.springframework.web.util.UriComponentsBuilder; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * @fileName : SocialLoginSuccess * @author : kangtaegyung * @since : 2022/12/16 * description : 소셜 로그인 성공 후 처리할 클래스 * 알고리즘 * 1) 인증된 객체를 홀더에 저장 * 2) 인증된 유저 정보를 oAuth2User(소셜로그인 클래스) 에 저장, 소셜로그인은 oAuth2User 사용 * 3) 권한 정보 가져오기 * 4) provider 정보 : google, naver, kakao * 5) 구글/네이버 등 provider 마다 전달해주는 속성명과 구조가 틀림 * 구글 : { email: forbob@naver.com , name: 강태경 } * 네이버 : { response : { email: forbob@naver.com , id : abcdef } } * 6) 토큰 발행 * 7) 리다이렉션 페이지로 이동 : vue 로 jwt , 유저정보를 전송함 * <p> * 참고) 함수 * - UriComponentsBuilder.fromUriString("기본url") * .queryParam("키", 값) // 쿼리스트링 변수명, 값 */ @Slf4j @Component @RequiredArgsConstructor public class SocialLoginSuccess extends SimpleUrlAuthenticationSuccessHandler { private final JwtUtils jwtUtils; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { SecurityContextHolder.getContext().setAuthentication(authentication); // 1) OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal(); // 2) List<GrantedAuthority> authorities = new ArrayList(authentication.getAuthorities()); String codeName = authorities.get(0).toString(); // 3) 권한 String email = ""; OAuth2AuthenticationToken authToken = (OAuth2AuthenticationToken) authentication; // 4) provider 정보 switch (authToken.getAuthorizedClientRegistrationId()) { // 5) case "google": Map<String, Object> googleUser = oAuth2User.getAttributes(); email = (String) googleUser.get("email"); break; case "naver": Map<String, Object> naverUser = (Map<String, Object>) oAuth2User.getAttributes().get("response"); email = (String) naverUser.get("email"); break; } String jwt = jwtUtils.generateJwtToken(email); // 6) String targetUrl = UriComponentsBuilder.fromUriString("http://localhost:8080/auth-redirect") // 7) .queryParam("accessToken", jwt) .queryParam("email", email) .queryParam("codeName", codeName) .build().toUriString(); getRedirectStrategy().sendRedirect(request, response, targetUrl); } }
- DB 에 새사용자가 저장되면 웹토큰을 발급해서 프론트로 유저정보와 함께 전송합니다.
- 프론트 전송 주소는 http://localhost:8080/auth-redirect 입니다.
- 웹토큰, email, codeName(권한) 정보를 쿼리스트링 방식으로 전송합니다.
– SocialProviders
구글 / 네이버 로그인 ( Providers ) 에 따라 해당되는 함수가 실행될 수 있도록 작성된 클래스
package org.example.simpledms.security.oauth; import lombok.Builder; import lombok.Getter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.example.simpledms.model.entity.auth.Member; import java.util.Map; /** * @fileName : SocialProviders * @author : kangtaegyung * @since : 2022/12/16 * description : name 는 email 의 @ 앞부분을 잘라서 저장 * 각각 : email 저장된 속성명 * google : (String) socialUser.get("email") * naver : Map<String, Object> response = (Map<String, Object>) socialUser.get("response") 안에 email 속성이 있음 * (String) response.get("email") * kakao : (String) socialUser.get("account_email") */ @Getter public class SocialProviders { PasswordEncoder encoder = new BCryptPasswordEncoder(); private Map<String, Object> socialUser; private String socialKey; private String name; private String email; @Builder public SocialProviders(Map<String, Object> socialUser, String socialKey, String name, String email) { this.socialUser = socialUser; this.socialKey = socialKey; this.name = name; this.email = email; } // registrationId 를 체크해서 구글 / 네이버 / 카카오 정보 가져오기 public static SocialProviders of(String registrationId, String nameAttributeName, Map<String, Object> socialUser) { switch (registrationId) { case "google": return ofGoogle(nameAttributeName, socialUser); case "naver": return ofNaver(nameAttributeName, socialUser); case "kakao": return ofKakao(nameAttributeName, socialUser); default: return ofGoogle(nameAttributeName, socialUser); } } private static SocialProviders ofGoogle(String nameAttributeName, Map<String, Object> socialUser) { return SocialProviders.builder() .name(((String) socialUser.get("email")).split("@")[0]) .email((String) socialUser.get("email")) .socialUser(socialUser) .socialKey(nameAttributeName) .build(); } private static SocialProviders ofNaver(String nameAttributeName, Map<String, Object> socialUser) { Map<String, Object> response = (Map<String, Object>) socialUser.get("response"); return SocialProviders.builder() .name(((String) response.get("email")).split("@")[0]) .email((String) response.get("email")) .socialUser(socialUser) .socialKey(nameAttributeName) .build(); } private static SocialProviders ofKakao(String nameAttributeName, Map<String, Object> socialUser) { Map<String, Object> kakaoAccount = (Map<String, Object>) socialUser.get("kakao_account"); return SocialProviders.builder() .name(((String) kakaoAccount.get("email")).split("@")[0]) .email((String) kakaoAccount.get("email")) .socialUser(socialUser) .socialKey(nameAttributeName) .build(); } // OAuthsocialUser에서 엔티티를 생성하는 시점은 처음 가입할 때 // User 엔티티를 생성 public Member getDefaultUser() { return Member.builder() .name(this.email) .email(this.email) .password(encoder.encode("123456")) .codeName("ROLE_USER") .build(); } }
- registrationId : 구글 인지 네이버 공급자( Providers ) 인지 알려주는 변수입니다.
- 공급자가 인증토큰과 함께 전송해 줍니다.
- 저장된 값은 google 또는 naver 가 저장되어 있습니다.
Vue 페이지 :
프론트는 로그인 버튼에 네이버 인증 요청 링크를 추가하는 것 외에 싱글 공급자 로그인 예제와 거의 동일합니다.
– Vue 패키지 추가 :
npm i axios
1) 공통 js
– utils/axiosDefaultConfig.js : axios 기본 설정 파일
import axios from "axios"; // axios 기본 설정 export default axios.create({ baseURL: "http://localhost:8000/api", headers: { "Content-Type": "application/json" } });
– router/index.js
import { createRouter, createWebHistory } from "vue-router"; const routes = [ { path: "/", component: () => import("../views/HomeView.vue"), }, // 로그인 { path: "/login", component: () => import("../views/auth/LoginView.vue"), }, // 회원가입 { path: "/register", component: () => import("../views/auth/RegisterView.vue"), }, // 소셜 로그인 { path: "/auth-redirect", component: () => import("../views/auth/SocialRedirectView.vue"), }, ]; const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes, }); export default router;
Vue 페이지
로그인 결과 화면 :
– views/auth/LoginView.vue
<!-- 사용법 : @submit.prevent="함수" --> <!-- prevent : submit 의 기본 속성을 막기(다른 곳으로 이동하려는 특징) --> <template> <div> <div class="row justify-content-center"> <div class="col-xl-10 col-lg-12 col-md-9"> <div class="card mt-5"> <div class="card-body p-0"> <!-- {/* Nested Row within Card Body */} --> <div class="row"> <div class="col-lg-6 bg-login-image"></div> <div class="col-lg-6"> <div class="p-5"> <div class="text-center"> <h1 class="h4 mb-4">Welcome Back!</h1> </div> <form class="user" @submit.prevent="login"> <div class="form-group"> <input type="email" class="form-control form-control-user mb-3" placeholder="이메일을 넣기" name="email" v-model="user.email" /> </div> <div class="form-group"> <input type="password" class="form-control form-control-user mb-3" placeholder="패스워드 넣기" name="password" v-model="user.password" /> </div> <button class="btn btn-primary btn-user w-100 mb-3"> Login </button> <hr /> <a href="http://localhost:8000/oauth2/authorization/google" class="btn btn-google btn-user w-100 mb-2" > <i class="fab fa-google fa-fw"></i> Login with Google </a> <a href="http://localhost:8000/oauth2/authorization/naver" class="btn btn-naver btn-user w-100 mb-2"> <i class="fa-solid fa-n"></i> Login with Naver </a> <a href="/" class="btn btn-kakao btn-user w-100 mb-3"> <i class="fa-solid fa-k"></i> Login with Kakao </a> </form> <hr /> <div class="text-center"> <a class="small" href="/forgot-password"> Forgot Password? </a> </div> <div class="text-center"> <a class="small" href="/register"> Create an Account! </a> </div> </div> </div> </div> </div> </div> </div> </div> </div> </template> <script> // TODO: 1) spring 보내준 user 객체(웹토큰있음)를 로컬스토리지에 저장 // TODO: 사용법 : localStorage.setItem(키, 값); // TODO: => 단, 값은 문자열만 저장됨 // TODO: 사용법 : JSON.stringify(객체) => 문자열로 바뀐 객체가 리턴됨 // TODO: 2) 공유저장소의 state / mutations 함수 접근법 // TODO: mutations 사용법 : this.$store.commit("함수명", 저장할객체) // TODO: => 로그인성공 공유함수(loginSuccess(state, 유저객체)) 실행 // TODO: state 사용법 : this.$store.state.공유속성명 // TODO: => 공유저장소의 공유속성 접근법 // TODO: 3) 뷰의 라이프사이클 // TODO: - mounted() : 화면이 뜰때 자동 실행 (생명주기 함수) // TODO: - created() : 뷰가 생성될대 자동 실행 // TODO: - created()(1번, 뷰만 생성되면 실행) -> mounted()(2번, html 태그까지 모두 뜰때) // TODO: 예) destoryed() : 뷰가 삭제될때 실행 (거의 사용 않함) import AuthService from "@/services/auth/AuthService"; export default { data() { return { user: { email: "", // 로그인ID password: "", }, }; }, methods: { async login() { try { let response = await AuthService.login(this.user); console.log(response.data); localStorage.setItem("user", JSON.stringify(response.data)); // 1) this.$store.commit("loginSuccess", response.data); // 2) this.$router.push("/"); } catch (e) { this.$store.commit("loginFailure"); console.log(e); } }, }, // 화면이 뜰때 실행되는 함수 created() { if (this.$store.state.loggedIn == true) { // 로그인 상태이면 로그인 불필요 this.$router.push("/"); } }, }; </script> <style> @import "@/assets/css/login.css"; </style>
- 로그인 버튼 url : 구글 API 주소 (http://localhost:8000/oauth2/code/google)
- 상기 주소를 구글 로그인 API 신청 시 클라우드 사이트에 등록해야 합니다.
Vue 리다이렉트 페이지
– views/auth/SocialRedirectView.vue
<!-- 소셜로그인 리다이렉트 페이지 --> <template> <div > </div> </template> <script> export default { data() { return { user: {}, }; }, mounted() { let url = new URL(window.location.href); console.log(url); const urlParams = url.searchParams; // uri 정보 가져오기 const accessToken = urlParams.get("accessToken"); const email = urlParams.get("email"); const codeName = urlParams.get("codeName"); this.user = { accessToken: accessToken, email: email, codeName: codeName, }; console.log("social user", this.user); localStorage.setItem("user", JSON.stringify(this.user)); this.$store.commit('loginSuccess', this.user); this.$router.push("/"); }, }; </script>
- 벡엔드에서 소셜 로그인 성공하면 Vue. 로 웹토큰, 유저정보를 보내줍니다.
- 로컬스토리지에 유저정보를 저장하고, Home 으로 강제 페이지 이동 시킵니다.
📃 결론
구글 / 네이버 로그인을 이용해서 소셜 로그인을 진행하는 예제를 살펴보았습니다.
구글 로그인 버튼을 클릭하면 구글 로그인 url 로 이동되고, 네이버 로그인 버튼을 클릭하면 네이버 로그인 url 로 이동됩니다.
구글 / 네이버 인증이 완료되면 인증토큰을 springboot 으로 전송합니다.
인가코드 및 인증토큰을 받는 로직은 OAuth 스프링 패키지가 자동적으로 처리합니다.
개발자는 인증토큰을 받은 후에 새 사용자를 DB 에 생성하고 웹토큰을 vue 전송하는 로직만 작성합니다.
Spring Boot 는 @RestController 어노테이션을 이용해 구현했으며, 결과는 JSON 데이터로 리턴됩니다.
Vue 는 axios 라이브러리를 사용해 벡엔드와 통신합니다.
DB 프레임워크는 JPA 를 이용해서 sql 문을 직접 제작하지 않고 자동화기능을 이용해 구현했습니다.
다중 로그인에 관심 있으시다면 Source 는 아래에서 찾을 수 있습니다.
감사합니다.