일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 한 번만 실행
- JavaScript
- Font
- crashlog
- MFC
- 블루투스 헤드셋
- VS2008
- 설치제거
- M8200
- GDI
- API
- protobuf-c
- 크래시로그
- 기념일관리
- 자바스크립트
- plcrashreporter
- PDA
- docker
- self-signed ssl
- php
- 데이터 전달
- EUC-KR
- C/C++
- phpmailer
- .net
- 와이브로
- ClickOnce
- Antialiasing
- C#
- net
- Today
- Total
~☆~ 우하하!!~ 개발블로그
[SpringBoot][소셜로그인] Facebook 으로 로그인/회원가입 본문
Facebook 계정으로 로그인을 구현하기 위해서 선행되어야 할 작업은 Facebook 개발자 센터에서 앱 설정 작업이다. 이 작업 내용은 https://iwoohaha.tistory.com/342 을 참고하면 된다.
application.yml 에 기록해야 할 설정값의 내용은 Google 로그인의 경우와 비슷하다. 비교를 위해서 Google 의 설정값을 함께 예시로 보여주고 있다.
spring:
security:
oauth2:
client:
registration:
facebook:
client-id: # Facebook 애플리케이션 ID
client-secret: # Facebook 애플리케이션 Secret
scope:
- public_profile
- email
google:
client-id: 528898101025-895po5vpn68pdkmuqj4aq314h2h0eob4.apps.googleusercontent.com
client-secret: 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
scope:
- profile
- email
# - openid
login.html 페이지에 Facebook 로그인하기 버튼을 추가한다. 앞서 작성한 Google, Naver, Kakao 로그인하기 버튼에 사용된 각 소셜 로그인 로고 이미지를 svg 파일 사용 방식으로 변경하고, 버튼에 사용된 css 스타일도 조금 수정해보았다.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.tyhmeleaf.org">
<head>
<meta charset="UTF-8">
<!-- 모바일에서의 적절한 반응형 동작을 위해 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- -->
<title>Diary</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
crossorigin="anonymous"
>
<!-- -->
<!-- head 태그에 다음의 코드를 삽입 -->
<style type="text/css">
html,
body {
height: 100%;
}
.form-signin {
max-width: 330px;
padding: 1rem;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
div {
margin: 0px;
padding: 0px;
}
.css-1n7nx3r {
margin-top: 24px;
}
.css-1n7nx3r::before {
color: rgb(94, 108, 132);
content: attr(data-i18n-continue);
display: block;
font-size: 14px;
line-height: 16px;
margin-bottom: 16px;
font-weight: 600;
text-align: center;
}
.css-1vymulm:not(:last-child) {
margin-bottom: 8px;
}
button {
font-family: inherit;
}
.css-1ti50tg {
opacity: 1;
transition: opacity 0.3s;
display: flex;
margin: 0px 2px;
-webkit-box-flex: 0;
flex-grow: 0;
flex-shrink: 0;
align-self: center;
font-size: 0px;
line-height: 0;
user-select: none;
margin-inline-start: var(--ds-space-negative-025, -2px);
}
img {
margin: 0px;
padding: 0px;
}
.css-178ag6o {
opacity: 1;
transition: opacity 0.3s;
margin: 0px 2px;
-webkit-box-flex: 1;
flex-grow: 1;
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.css-google-button {
-webkit-box-align: baseline;
align-items: baseline;
box-sizing: border-box;
display: inline-flex;
font-size: inherit;
font-style: normal;
font-family: inherit;
max-width: 100%;
position: relative;
text-align: center;
text-decoration: none;
transition: background 0.1s ease-out, box-shadow 0.15s cubic-bezier(0.47, 0.03, 0.49, 1.38);
white-space: nowrap;
cursor: pointer;
padding: 0px 10px;
vertical-align: middle;
width: 100%;
-webkit-box-pack: center;
justify-content: center;
box-shadow: none;
font-weight: bold;
border: 1px solid rgb(193, 199, 208);
border-radius: 3px;
color: var(--ds-text, #42526E) !important;
height: 40px !important;
line-height: 40px !important;
background: rgb(255, 255, 255) !important;
}
.css-naver-button {
-webkit-box-align: baseline;
align-items: baseline;
box-sizing: border-box;
display: inline-flex;
font-size: inherit;
font-style: normal;
font-family: inherit;
max-width: 100%;
position: relative;
text-align: center;
text-decoration: none;
transition: background 0.1s ease-out, box-shadow 0.15s cubic-bezier(0.47, 0.03, 0.49, 1.38);
white-space: nowrap;
cursor: pointer;
padding: 0px 10px;
vertical-align: middle;
width: 100%;
-webkit-box-pack: center;
justify-content: center;
box-shadow: none;
font-weight: bold;
border: 1px solid rgb(193, 199, 208);
border-radius: 3px;
color: var(--ds-text, #42526E) !important;
height: 40px !important;
line-height: 40px !important;
background: #04C75B !important;
}
.css-kakao-button {
-webkit-box-align: baseline;
align-items: baseline;
box-sizing: border-box;
display: inline-flex;
font-size: inherit;
font-style: normal;
font-family: inherit;
max-width: 100%;
position: relative;
text-align: center;
text-decoration: none;
transition: background 0.1s ease-out, box-shadow 0.15s cubic-bezier(0.47, 0.03, 0.49, 1.38);
white-space: nowrap;
cursor: pointer;
padding: 0px 10px;
vertical-align: middle;
width: 100%;
-webkit-box-pack: center;
justify-content: center;
box-shadow: none;
font-weight: bold;
border: 1px solid rgb(193, 199, 208);
border-radius: 3px;
color: var(--ds-text, #42526E) !important;
height: 40px !important;
line-height: 40px !important;
background: #fee500 !important;
}
.css-facebook-button {
-webkit-box-align: baseline;
align-items: baseline;
box-sizing: border-box;
display: inline-flex;
font-size: inherit;
font-style: normal;
font-family: inherit;
max-width: 100%;
position: relative;
text-align: center;
text-decoration: none;
transition: background 0.1s ease-out, box-shadow 0.15s cubic-bezier(0.47, 0.03, 0.49, 1.38);
white-space: nowrap;
cursor: pointer;
padding: 0px 10px;
vertical-align: middle;
width: 100%;
-webkit-box-pack: center;
justify-content: center;
box-shadow: none;
font-weight: bold;
border: 1px solid rgb(193, 199, 208);
border-radius: 3px;
color: var(--ds-text, #42526E) !important;
height: 40px !important;
line-height: 40px !important;
background: #1877F2 !important;
}
</style>
</head>
<body>
<main class="form-signin w-100 m-auto">
<th:block th:if="${error}">
<div th:if="${message}" class="alert alert-danger" role="alert" th:text="${message}"></div>
</th:block>
<!-- <form th:action="@{/process_login}" method="POST">-->
<form th:action="@{/signin}" method="POST">
<h1 class="h3 mb-3 fw-normal">Please sign in</h1>
<div class="form-floating">
<input type="email" class="form-control" id="floatingInput" name="email" placeholder="name@example.com">
<label for="floatingInput">Email address</label>
</div>
<div class="form-floating">
<input type="password" class="form-control" id="floatingPassword" name="password" placeholder="Password">
<label for="floatingPassword">Password</label>
</div>
<!-- <div class="form-check text-start my-3">-->
<!-- <input class="form-check-input" type="checkbox" value="yes" name="remember-me" id="flexCheckDefault">-->
<!-- <label class="form-check-label" for="flexCheckDefault">-->
<!-- Remember me-->
<!-- </label>-->
<!-- </div>-->
<div class="d-grid gap-2">
<button class="btn btn-primary w-100 py-2" type="submit">Sign in</button>
<a href="/signup">
<button class="btn btn-success w-100 py-2" type="button">Sign up</button>
</a>
<div data-i18n-or="또는" data-i18n-continue="또는 다음을 사용하여 계속하기" class="google-login social-login css-1n7nx3r" data-testid="social-login-wrapper" style="">
<div data-testid="social-login-button-row" class="css-1vymulm">
<div class="css-1vymulm">
<a href="/oauth2/authorization/google">
<button id="google-auth-button" class="css-google-button" tabindex="0" type="button">
<span class="css-1ti50tg">
<img src="/images/googlelogo.svg" alt="Google Logo" style="width: 24px; height: 24px; margin-right: 10px;">
</span>
<span>Google</span>
</button>
</a>
</div>
<div class="css-1vymulm">
<a href="/oauth2/authorization/naver">
<button id="naver-auth-button" class="css-naver-button" tabindex="0" type="button">
<span class="css-1ti50tg">
<img src="/images/naverlogo.svg" alt="Naver Logo" style="width: 34px; height: 34px; margin-right: 10px;">
</span>
<span>Naver</span>
</button>
</a>
</div>
<div class="css-1vymulm">
<a href="/oauth2/authorization/kakao">
<button id="kakao-auth-button" class="css-kakao-button" tabindex="0" type="button">
<span class="css-1ti50tg">
<img src="/images/kakaologo.svg" alt="Kakao Logo" style="width: 34px; height: 34px; margin-right: 10px;">
</span>
<span>Kakao</span>
</button>
</a>
</div>
<div class="css-1vymulm">
<a href="/oauth2/authorization/facebook">
<button id="facebook-auth-button" class="css-facebook-button" tabindex="0" type="button">
<span class="css-1ti50tg">
<img src="/images/facebooklogo.svg" alt="Facebook Logo" style="width: 24px; height: 24px; margin-right: 10px;">
</span>
<span style="color: white;">Facebook</span>
</button>
</a>
</div>
</div>
</div>
</div>
<p class="mt-5 mb-3 text-body-secondary">© 2017–2024</p>
</form>
</main>
<!-- Popper -->
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
crossorigin="anonymous"></script>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy"
crossorigin="anonymous"></script>
</body>
</html>
외관상으로는 큰 차이가 나지는 않지만, css 전문가가 아니다보니, 주먹구구식으로 가져다가 쓴 코드가 지저분해서 나름 조금 정리를 해본 셈이다.
새로 추가한 Facebook 버튼의 URL 은 /oauth2/authorization/facebook 이다.
여기에서 사용하는 각 소셜 로그인 버튼의 svg 파일은 zip 파일로 묶어서 올려둔다.
Facebook 버튼을 클릭했을 때 해당 정보제공자로부터 어떤 형식의 데이터가 전달되는지를 보려면 앞에서 해봤던 것처럼 CustomOAuth2UserService 의 loadUser 함수에 BreakPoint 를 걸고 Debug 모드로 실행시키면 된다.
위 스크린샷에서 볼 수 있는 것처럼 id, name, email 이라는 키값으로 이루어진 attributes 를 참고하면 되는 것으로 확인된다.
이런 구성의 데이터를 가져올 수 있는 FacebookOAuth2UserInfo 클래스를 아래와 같이 구현한다.
package com.woohahaapps.study.diary.domain;
import java.util.HashMap;
import java.util.Map;
public class FacebookOAuth2UserInfo implements OAuth2UserInfo {
private final Map<String, Object> attributes;
public FacebookOAuth2UserInfo(Map<String, Object> attributes) {
this.attributes = attributes;
}
@Override
public String getProvider() {
return "facebook";
}
@Override
public String getProviderId() {
return (String) attributes.get("id");
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
@Override
public String getProfileImageUrl() {
return "";
}
@Override
public Map<String, Object> convertToMap() {
Map<String, Object> map = new HashMap<>();
map.put("provider", getProvider());
map.put("providerid", getProviderId());
map.put("email", getEmail());
map.put("name", getName());
map.put("profileimageurl", getProfileImageUrl());
return map;
}
}
loadUser 함수에는 FacebookOAuth2UserInfo 클래스를 사용하는 코드를 추가한다.
...
OAuth2UserInfo oAuth2UserInfo = null;
if (provider.equals("google")) {
oAuth2UserInfo = new GoogleOAuth2UserInfo(oAuth2User.getAttributes());
} else if (provider.equals("naver")) {
oAuth2UserInfo = new NaverOAuth2UserInfo(oAuth2User.getAttribute("response"));
} else if (provider.equals("kakao")) {
oAuth2UserInfo = new KakaoOAuth2UserInfo(oAuth2User.getAttributes());
} else if (provider.equals("facebook")) {
oAuth2UserInfo = new FacebookOAuth2UserInfo(oAuth2User.getAttributes());
}
...
앞에서 해봤던 경험대로 여기까지만 추가한 뒤 실행시키면 나머지는 자동으로 처리될 것을 안다.
로그인 화면에서 Facebook 버튼을 클릭하면 facebook 로그인 화면으로 이동(Facebook 로그인이 되어 있지 않은 경우에만)되고,
facebook 로그인에 성공하면 로그인한 계정으로 계속할 것인지를 확인한다.
아직 Facebook 에 검수를 받지 못한 상태라서 나타나는 내용도 있는데, 이것은 상용 서버로 프로그램을 업로드하기 전에 해결해야 할 내용이다.
"승우님으로 계속" 버튼을 클릭하면 전달받은 이메일 주소로 회원가입이 필요한 경우 회원가입이 처리되고, 해당 이메일 계정으로 로그인된다.
'SpringBoot' 카테고리의 다른 글
[SpringBoot][소셜로그인] 정리. github 프로젝트 diary (0) | 2024.11.28 |
---|---|
[SpringBoot][소셜로그인] Apple 계정으로 로그인하기 (0) | 2024.11.25 |
[SpringBoot][소셜로그인] kakao 소셜 로그인 기능 구현 (1) | 2024.11.22 |
[SpringBoot][소셜로그인] 소셜 로그인 (Naver) 및 회원가입 절차 수정 (0) | 2024.11.20 |
[SpringBoot][소셜로그인] Naver 로그인 구현 (1차 시도) (0) | 2024.11.19 |