Symfony로 페이스북 로그인

내 목표는 기존 Symfony 애플리케이션에 Facebook으로 계속하기(로그인) 버튼을 추가하는 것이었습니다. 여기에 전체 코드를 제공하지 않습니다. Facebook 로그인 버튼을 만드는 주요 부분만. 여기 내가 한 일이 있습니다.

개요



워크플로 개요는 다음과 같습니다.
  • 사용자가 Facebook 로그인 버튼을 클릭함
  • Facebook 인증 사용자
  • 앱이 Facebook 토큰(인증 데이터)을 Symfony 백엔드로 보냅니다.
  • 백엔드는 인증 데이터를 사용하여 사용자 데이터(이름 및 이메일)를 가져오고 Symfony 처리기로 로그인합니다.

  • 따라서 Facebook은 로그인하려는 사람을 확인하는 첫 번째 부분에만 필요합니다.

    백엔드에서 사용자를 인증하기 위해 Guard을 사용합니다.

    페이스북 라이브러리



    새 앱 만들기here .

    프론트엔드용 Facebook Javascript SDK와 백엔드용 PHP 라이브러리가 필요합니다.
    PHP 페이스북 라이브러리 설치:

    composer require facebook/graph-sdk
    


    템플릿에 Facebook Javascript SDK를 포함합니다. 다음과 같이 표시되어야 합니다.

    <script async defer crossorigin="anonymous"
            src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v7.0&appId=<your app id here>">
    </script>
    


    가장 간단한 방법은 Facebook 로그인 - 빠른 시작 섹션의 Facebook developers 페이지에서 이 스니펫을 생성하는 것입니다.

    프론트엔드



    다음으로 버튼을 추가하고 로그인 결과를 처리해야 합니다. 버튼 예:

    <div class="fb-login-button"
         data-size="large"
         data-button-type="continue_with"
         data-layout="default"
         data-auto-logout-link="false"
         data-use-continue-as="false"
         data-scope="public_profile,email"
         onlogin="fbGetLoginStatus();"
         data-width=""></div>
    
    


    Here은 버튼 구성기입니다.

    해당 코드로 Javascript 모듈을 만들었습니다.

    window.fbGetLoginStatus = function () {
        FB.getLoginStatus(function (response) {
            if (isConnected(response)) {
                fbLogIn(response);
            }
        });
    }
    function isConnected(response) {
        return response.status === 'connected';
    }
    
    function fbLogIn(response) {
        let loginForm = document.querySelector('.login-form');
        let input = getHiddenInput("fbAuthResponse", JSON.stringify(response.authResponse));    
        loginForm.appendChild(input);
        loginForm.submit();
    }
    
    function getHiddenInput(name, value) {
        let input = document.createElement("input");
        input.setAttribute("type", "hidden");
        input.setAttribute("name", name);
        input.setAttribute("value", value);
    }
    


    사용자가 *Facebook으로 계속하기 버튼을 클릭하고 성공적으로 로그인하면 로그인(또는 등록) 양식을 제출하고 POST 요청을 통해 백엔드로 데이터를 보냅니다.

    백엔드 - Symfony 가드



    일반적인 로그인 비밀번호 인증을 위해 이미 하나의 Guard가 있었습니다. 그래서 두 번째를 추가합니다. Guard에 대한 security.yaml 구성은 다음과 같습니다.

    security:
        firewalls:
            main:
                guard:
                    authenticators:
                        - App\Security\LoginFormAuthenticator
                        - App\Security\FacebookAuthenticator
                    entry_point: App\Security\LoginFormAuthenticator
    


    이제 AbstractFormLoginAuthenticator를 확장하는 Guard 파일을 만들어야 합니다.

    class FacebookAuthenticator extends AbstractFormLoginAuthenticator {}
    


    FacebookAuthenticator의 주요 메서드는 다음과 같습니다.

    지원 - 해당 Guard를 사용해야 하는지 확인합니다. 로그인 및 등록 페이지에 동일한 논리를 사용했습니다. 그래서 그것은 다음과 같이 보입니다.

    public function supports(Request $request)
    {
        $route = $request->attributes->get('_route');
        $isLoginOrRegister = in_array($route, ['app_login', 'app_register']);
        return $isLoginOrRegister 
            && $request->isMethod('POST') 
            && $request->get('fbAuthResponse');
    }
    


    getCredentials - Facebook 인증 응답을 가져와 배열로 디코딩합니다.

    public function getCredentials(Request $request)
    {
        return json_decode($request->get('fbAuthResponse'), true);
    }
    


    onAuthenticationSuccess - 사용자를 방문 페이지로 리디렉션합니다.

    public function onAuthenticationSuccess(
        Request $request, 
        TokenInterface $token, 
        $providerKey
    )
    {
        return new RedirectResponse(
            $this->urlGenerator->generate('app_landing')
        );
    }
    


    URL 생성기를 사용하려면 생성자에 추가합니다(아래 참조).

    getUser - Facebook 그래프 API에서 이메일과 이름을 가져오고 사용자 엔터티를 반환하는 주요 부분:

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        if (empty($credentials['accessToken'])) {
            throw new CustomUserMessageAuthenticationException('Your message here');
        }
    
        $fbUser = $this->fbService->getUser($credentials['accessToken']);
    
        if (empty($fbUser->getEmail())) {
            throw new CustomUserMessageAuthenticationException('Your message here');
        }
    
        return $userProvider->loadUserByUsername($fbUser->getEmail());
    }
    


    나는 사용자가 없을 때 등록을 위해 이메일과 이름을 사용합니다. 여기에서는 편의를 위해 등록하지 않고 로그인 부분만 있습니다.

    Guard 생성자에서 서비스를 자동 연결하는 방법은 다음과 같습니다.

    public function __construct(
        FacebookService $fbService, 
        UrlGeneratorInterface $urlGenerator
    )
    {
        $this->fbService = $fbService;
        $this->urlGenerator = $urlGenerator;
    }
    


    내 fbService는 다음과 같습니다.

    <?php
    
    namespace App\Service;
    
    use App\Entity\FacebookUser;
    use Facebook\Facebook;
    
    class FacebookService
    {
        private $client;
    
        public function __construct(
            string $fbAppId, 
            string $fbAppSecret, 
            string $fbGraphVersion
        )
        {
            $this->client = new Facebook([
                'app_id' => $fbAppId,
                'app_secret' => $fbAppSecret,
                'default_graph_version' => $fbGraphVersion,
            ]);
    
        }
    
        public function getUser(string $token): FacebookUser
        {
            $user = new FacebookUser();
    
            try {
                $fbUser = $this->client->get("/me?fields=name,email", $token);
                $data = $fbUser->getDecodedBody();
                $user
                    ->setName($data['name'])
                    ->setEmail($data['email']);
            } catch (\Throwable $exception) {
                // handle exception here
            }
    
            return $user;
        }
    }
    


    FacebookService 생성자 인수를 자동 연결하려면 환경에 따라 .env 파일에 변수를 추가하고 services.yaml 구성에 매개변수를 추가합니다.

    services:
        App\Service\FacebookService:
            arguments:
                $fbAppId: '%env(FB_APP_ID)%'
                $fbAppSecret: '%env(FB_APP_SECRET)%'
                $fbGraphVersion: '%env(FB_GRAPH_VERSION)%'
    


    다음은 FacebookUser 엔터티입니다. 엔티티 대신 배열을 사용할 수 있지만 OOP가 더 편리합니다.

    <?php
    
    namespace App\Entity;
    
    class FacebookUser
    {
        private $name;
        private $email;
    
        public function getName(): ?string
        {
            return $this->name;
        }
    
        public function setName($name): self
        {
            $this->name = $name;
            return $this;
        }
    
        public function getEmail(): ?string
        {
            return $this->email;
        }
    
        public function setEmail(string $email): self
        {
            $this->email = $email;
            return $this;
        }
    }
    


    그게 다야

    사실 Guard에는 더 많은 메소드가 있지만 생성하는 것이 복잡하지 않아야 합니다.

    좋은 웹페이지 즐겨찾기