본문 바로가기
공부/Spring

[Spring] 스프링 OAuth2 페이스북 로그인 (OAuth2 스프링 3편)

by 웅대 2023. 3. 30.
728x90

 

이전 구글 로그인에 이어서 페이스북 로그인도 진행해보려 한다.

https://growth-coder.tistory.com/136

 

[Spring][Spring] 스프링 Oauth2 구글 로그인과 jpa 사용하여 유저 정보 데이터베이스에 저장 및 권한 설

https://growth-coder.tistory.com/135 이전 포스팅에 이어서 구글로부터 받은 정보를 데이터베이스에 저장하고 권한 설정을 해보려한다. 이전 포스팅에서 jpa 및 데이터베이스 세팅을 해뒀기 때문에 이번

growth-coder.tistory.com

 

페이스북 개발자 센터로 들어가서 로그인 하고 우측 상단의 Get Started로 들어간다.

https://developers.facebook.com/ 

 

Meta for Developers

꿈의 아틀리에 창조 BUCK의 크리에이터와 개발자로부터 Meta Spark를 사용하여 DIOR Beauty를 위한 AR 경험을 설계 및 빌드하는 과정에 대한 비하인드 스토리를 들어보세요. 이제 고급 액세스에 대한 비

developers.facebook.com

 

 

인증 과정의 순서대로 진행한다.

모두 진행하고 나면 앱을 생성할 수 있다. 앱 만들기를 눌러서 앱을 생성한다.

앱 유형을 소비자로 선택한다.

앱 이름을 정하고 생성한다.

앱이 생성되고 나면 제품을 추가해야한다.

페이스북 로그인을 할 것이기 때문에 페이스북 로그인을 설정한다.

웹 페이지에 사용해야하므로 웹을 선택한다.

 로컬 환경에서 스프링 부트를 띄워 진행할 예정이므로 스프링 부트의 기본 url을 입력해준다.

이후 여러 단계들이 있는데 일단 모두 스킵한다.

이제 좌측 메뉴의 기본 설정으로 들어간다.

앱 ID와 시크릿 코드를 확인할 수 있는데 이를 application.properties에 입력해줘야한다.

이전 구글 로그인과 형식은 똑같다.

 

<application.properties>

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/[데이터베이스 이름]
spring.datasource.username=root
spring.datasource.password=[비밀번호]
spring.profiles.include=oauth

spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.hibernate.ddl-auto=update

spring.security.oauth2.client.registration.google.client-id=[클라이언트 아이디]
spring.security.oauth2.client.registration.google.client-secret=[클라이언트 시크릿]
spring.security.oauth2.client.registration.google.scope=profile, email

spring.security.oauth2.client.registration.facebook.client-id=[앱 아이디]
spring.security.oauth2.client.registration.facebook.client-secret=[앱 시크릿 코드]
spring.security.oauth2.client.registration.facebook.scope=public_profile, email

이제 페이스북 로그인을 진행할 버튼을 resource/templates 폴더 하위에 loginForm.html에 추가해준다.

 

url은 "/oauth2/authorization/facebook"이다.

 

<loginForm.html>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>로그인</title>
</head>
<body>
<form action="/login" method="post">
    <input type="text" name="name"> <br>
    <input type="password" name="password">
    <button>제출</button>
</form>
<a href="/oauth2/authorization/google">google login</a>
<a href="/oauth2/authorization/facebook">facebook login</a>
<a href="/joinForm">회원가입 하러 가기 </a>
</body>
</html>

이전 포스팅에서 DefaultOAuth2UserService를 확장한 클래스의 loadUser 함수가 provider 로부터 받아온 userRequest를 처리해준다고 배웠다.

 

구글과 페이스북의 userRequest와 OAuth2User의 attributes의 형태는 거의 동일하나 약간의 차이가 존재한다.

google의 attributes 형태는 다음과 같다. 

{
   sub=111111111111,
   name=홍길동,
   given_name=길동,
   family_name홍,
   picture=[이미지 url]
   email=email@gmail.com,
   email_verified=true,
   locale=ko
}

facebook의 attributes 형태는 다음과 같다.

{
   id=107956215586143,
   name=김철수,
   email=adgildong1@gmail.com
}

같은 의미를 가지는 값의 key 값이 서로 다르기 때문에 이전에 작성한 구글 로그인을 그대로 사용하면 문제가 발생할 수 있다.

 

구글과 페이스북을 따로 처리하기 위해서 인터페이스를 생성한다.

public interface OAuth2MemberInfo {
    String getProviderId(); //공급자 아이디 ex) google, facebook
    String getProvider(); //공급자 ex) google, facebook
    String getName(); //사용자 이름 ex) 홍길동 
    String getEmail(); //사용자 이메일 ex) gildong@gmail.com
}

이제 구글과 페이스북에 대해서 이 인터페이스를 각각 구현하기만 하면 된다.

그리고 각각의 구현체는 attributes를 가지고 있어야 한다.

이제 각각의 구현체를 생성할 차례이다.

 

<GoogleMemberInfo>

public class FacebookMemberInfo implements OAuth2MemberInfo{
    private Map<String, Object> attributes;
    @Override
    public String getProviderId() {
        return (String) attributes.get("id");
    }

    @Override
    public String getProvider() {
        return "facebook";
    }

    @Override
    public String getName() {
        return (String) attributes.get("name");
    }

    @Override
    public String getEmail() {
        return (String) attributes.get("email");
    }
}

<FacebookMemberInfo>

public class FacebookMemberInfo implements OAuth2MemberInfo{
    private Map<String, Object> attributes;
    @Override
    public String getProviderId() {
        return (String) attributes.get("id");
    }

    @Override
    public String getProvider() {
        return "facebook";
    }

    @Override
    public String getName() {
        return (String) attributes.get("name");
    }

    @Override
    public String getEmail() {
        return (String) attributes.get("email");
    }
}

 

이제 provider에 따라 구현체를 달리 사용하면 된다.

 

loadUser 메소드를 다음과 같이 고치면 된다.

@Service
@RequiredArgsConstructor
public class OAuth2MemberService extends DefaultOAuth2UserService {
    private final BCryptPasswordEncoder encoder;
    private final MemberRepository memberRepository;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);
        OAuth2MemberInfo memberInfo = null;
        System.out.println(userRequest.getClientRegistration().getRegistrationId());
        if (userRequest.getClientRegistration().getRegistrationId().equals("google")) {
            memberInfo = new GoogleMemberInfo(oAuth2User.getAttributes());
        } else if (userRequest.getClientRegistration().getRegistrationId().equals("facebook")) {
            memberInfo = new FacebookMemberInfo(oAuth2User.getAttributes());
        } else {
            System.out.println("로그인 실패");
        }
        String provider = memberInfo.getProvider();
        String providerId = memberInfo.getProviderId();
        String username = provider + "_" + providerId; //중복이 발생하지 않도록 provider와 providerId를 조합
        String email = memberInfo.getEmail();
        String role = "ROLE_ADMIN"; //일반 유저
        System.out.println(oAuth2User.getAttributes());
        Optional<Member> findMember = memberRepository.findByName(username);
        Member member=null;
        if (findMember.isEmpty()) { //찾지 못했다면
            member = Member.builder()
                    .name(username)
                    .email(email)
                    .password(encoder.encode("password"))
                    .role(role)
                    .provider(provider)
                    .providerId(providerId).build();
            memberRepository.save(member);
        }
        else{
            member=findMember.get();
        }
        return new PrincipalDetails(member, oAuth2User.getAttributes());
    }
}

 

 

참고

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0/dashboard

 

[무료] 스프링부트 시큐리티 & JWT 강의 - 인프런 | 강의

스프링부트 시큐리티에 대한 개념이 잡힙니다., - 강의 소개 | 인프런

www.inflearn.com

 

728x90
반응형

댓글