1. Meta(구 Facebook) developers에 들어가서 새 앱을 만들어준다.
없음 누르고 앱 ID 아무렇게나 입력하도 만들면 된다.
2. 페이스북 로그인 제품 추가
웹을 누르고 넘어간다.
자바스크립트가 아니라 OAuth2 클라이언트로 구현할 것이기 때문에 URL만 입력하고 나머지는 넘어간다.
3. 설정 -> 기본 설정에 들어가서 앱 ID와 시크릿 코드를 확인한다.
4. application.yml 수정
들여쓰기를 맞춰서 id와 코드를 넣어준다.
https://developers.facebook.com/docs/facebook-login/web/permissions
이 문서를 보면 scope는 이름이 public_profile로 되어있기 때문에 public_profile을 넣어줘야 한다.
5. loginForm 수정
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인 페이지</title>
</head>
<body>
<h1>로그인 페이지</h1>
<hr>
<form action="/login" method="POST">
<input type="text" name="username" placeholder="Username"> <br>
<input type="password" name="password" placeholder="Password"> <br>
<button>로그인</button>
</form>
<a href="/oauth2/authorization/google">구글 로그인</a>
<a href="/oauth2/authorization/facebook">페이스북 로그인</a> //추가함
<a href="/joinForm">회원가입을 아직 하지 않으셨나요?</a>
</body>
</html>
6. 페이스북 로그인을 클릭해본다.
제대로 로그인이 되는 것을 확인할 수 있다.
하지만 DB에서 확인해보면 providerId가 null인 것을 알 수 있다.
왜냐하면 구글 로그인에서는 getAttribute("sub")가 있었지만 페이스북은 그 자리가 getAttribute("id")가 되어야 하기 때문이다. (나는 그래서 PrincipalOauth2UserService에서 그냥 provider만 분기처리 해서 그대로 쓰는 방식을 생각했었다.)
유지보수의 용이성을 위해 우리는 OAuth2UserInfo라는 인터페이스를 만들거다.
인터페이스를 만들어두면 네이버나 카카오 등의 다른 OAuth 인증 방식을 추가할 때 덜 헷갈리고 코드가 덜 복잡해진다.
package com.pure.security.config.oauth.provider;
public interface OAuth2UserInfo {
String getProviderId();
String getProvider();
String getEmail();
String getName();
}
OAuth2UserInfo를 구현하는 GoogleUserInfo와 FacebookUserInfo 클래스를 만든다.
package com.pure.security.config.oauth.provider;
import java.util.Map;
public class GoogleUserInfo implements OAuth2UserInfo {
private Map<String, Object> attributes;
public GoogleUserInfo(Map<String, Object> attributes) {
this.attributes = attributes;
}
@Override
public String getProviderId() {
return (String)attributes.get("sub");
}
@Override
public String getProvider() {
return "google";
}
@Override
public String getEmail() {
return (String)attributes.get("email");
}
@Override
public String getName() {
return (String)attributes.get("name");
}
}
package com.pure.security.config.oauth.provider;
import java.util.Map;
public class FacebookUserInfo implements OAuth2UserInfo {
private Map<String, Object> attributes;
public FacebookUserInfo(Map<String, Object> attributes) {
this.attributes = attributes;
}
@Override
public String getProviderId() {
return (String)attributes.get("id");
}
@Override
public String getProvider() {
return "facebook";
}
@Override
public String getEmail() {
return (String)attributes.get("email");
}
@Override
public String getName() {
return (String)attributes.get("name");
}
}
이를 이용하여 로그인/회원가입 로직을 다시 쓴다.
PrincipalOauth2UserService 수정
package com.pure.security.config.oauth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import com.pure.security.config.auth.PrincipalDetails;
import com.pure.security.config.oauth.provider.FacebookUserInfo;
import com.pure.security.config.oauth.provider.GoogleUserInfo;
import com.pure.security.config.oauth.provider.OAuth2UserInfo;
import com.pure.security.model.User;
import com.pure.security.repository.UserRepository;
@Service
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {
@Autowired
private UserRepository userRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
System.out.println("userRequset.getClientRegistration: "+ userRequest.getClientRegistration()); //registrationId로 어떤 OAuth로 로그인 했는지 알 수 있음.
System.out.println("userRequset.getAccessToken: "+ userRequest.getAccessToken());
System.out.println("userRequest.getAttributes: "+ super.loadUser(userRequest).getAttributes());
OAuth2User oauth2User = super.loadUser(userRequest);
String provider = userRequest.getClientRegistration().getRegistrationId(); //google?
OAuth2UserInfo oAuth2UserInfo = null;
if(provider.equals("google")) {
System.out.println("구글 로그인 요청");
oAuth2UserInfo = new GoogleUserInfo(oauth2User.getAttributes());
}else if(provider.equals("facebook")) {
System.out.println("페이스북 로그인 요청");
oAuth2UserInfo = new FacebookUserInfo(oauth2User.getAttributes());
}else {
System.out.println("현재 구글과 페이스북만 지원됩니다.");
}
String providerId = oAuth2UserInfo.getProviderId();
String username = provider+"_"+oAuth2UserInfo.getProviderId();
String password = new BCryptPasswordEncoder().encode("1234");
String email = oauth2User.getAttribute("email");
String role = "ROLE_USER";
//해당 username이 이미 있는지 확인
User userEntity = userRepository.findByUsername(username);
//구글로 처음 로그인 하면 자동으로 회원가입 시킴.
if(userEntity == null) {
System.out.println("최초 구글 로그인으로 회원가입을 진행했습니다.");
userEntity = User.builder()
.username(username)
.password(password)
.email(email)
.role(role)
.provider(provider)
.providerId(providerId)
.build();
userRepository.save(userEntity);
}else {
System.out.println("회원가입 이력이 있습니다. 자동 로그인을 진행했습니다.");
}
return new PrincipalDetails(userEntity, oauth2User.getAttributes()); //이 PrincipalDetails가 Authentication 객체 안에 들어가게 됨.
}
}
이렇게 하면 제대로 작동하는 것을 확인할 수 있다.
'취업 준비 > Spring security' 카테고리의 다른 글
10. TCP는 무엇인가? (0) | 2022.02.24 |
---|---|
9. 네이버 OAuth 로그인 (0) | 2022.02.24 |
7. PrincipalDetails 객체를 이용하여 2가지 로그인 방식 통합 (0) | 2022.02.17 |
6. OAuth2 구글 로그인 (0) | 2022.02.17 |
5. 시큐리티 권한처리 (0) | 2022.02.16 |