iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 25
0
Modern Web

寫給朋友的 PHP 從 0 到 100 實戰教程系列 第 25

Day 25. PHP教學: 實作臉書帳號登入後的註冊

今天來實作臉書登入後,幫使用者建立帳號並登入的步驟
取得資料的方法是:

$fb->setDefaultAccessToken($accessToken);
$response = $fb->get('/me?locale=en_US&fields=id,name,email');
$userNode = $response->getGraphUser();
$email = $userNode->getField('email');
$fb_user_id = $userNode->getField('id');

先來做一些變數整理:
把 app_id, app_secret 與 fb_callback_url 整理到我們的 config 吧
記得改寫成你自己申請的 id 與 secret

config/Config.php

 class Config {
     const BASE_URL = "http://127.0.0.1/login_register";
     const MAIL_FROM = "no_reply@mail.com";
     const MAIL_FROM_NAME = "http://127.0.0.1/login_register/";
     const MAIL_USER_NAME = "your_gmail_account";
     const MAIL_USER_PASSWROD = "your_gmail_password";
     const FB_APP_ID = 'your_app_id';
     const FB_APP_SECRET = 'your_app_secret';
     const FB_CALLBACK_URL = 'http://localhost/game/fb-callback';
 }

再來把臉書基本宣告這段放到 init.php 裡面

<?php
date_default_timezone_set('Asia/Taipei');
if(isset($_POST))$_POST = GUMP::xss_clean($_POST);
$route = new Router(Request::uri()); //搭配 .htaccess 排除資料夾名稱後解析 URL
$msg = new \Plasticbrain\FlashMessages\FlashMessages();

$fb = new Facebook\Facebook([
    'app_id' => Config::FB_APP_ID, // Replace {app-id} with your app id
    'app_secret' => Config::FB_APP_SECRET,
    'default_graph_version' => 'v2.2',
    ]);

接著來修改這支專門接臉書的回傳程式吧,在後方加上檢查是否存在,不存在就自動註冊若存在就自動登入。

controller/fb-callback.php

<?php
  $helper = $fb->getRedirectLoginHelper();
  try {
    $accessToken = $helper->getAccessToken();
  } catch(Facebook\Exceptions\FacebookResponseException $e) {
    // When Graph returns an error
    echo 'Graph returned an error: ' . $e->getMessage();
    exit;
  } catch(Facebook\Exceptions\FacebookSDKException $e) {
    // When validation fails or other local issues
    echo 'Facebook SDK returned an error: ' . $e->getMessage();
    exit;
  }
  
  if (! isset($accessToken)) {
    if ($helper->getError()) {
      header('HTTP/1.0 401 Unauthorized');
      echo "Error: " . $helper->getError() . "\n";
      echo "Error Code: " . $helper->getErrorCode() . "\n";
      echo "Error Reason: " . $helper->getErrorReason() . "\n";
      echo "Error Description: " . $helper->getErrorDescription() . "\n";
    } else {
      header('HTTP/1.0 400 Bad Request');
      echo 'Bad request';
    }
    exit;
  }
  
  // The OAuth 2.0 client handler helps us manage access tokens
  $oAuth2Client = $fb->getOAuth2Client();
  
  // Get the access token metadata from /debug_token
  $tokenMetadata = $oAuth2Client->debugToken($accessToken);
  
  // Validation (these will throw FacebookSDKException's when they fail)
  $tokenMetadata->validateAppId(Config::FB_APP_ID); // Replace {app-id} with your app id
  // If you know the user ID this access token belongs to, you can validate it here
  //$tokenMetadata->validateUserId('123');
  $tokenMetadata->validateExpiration();
  
  if (! $accessToken->isLongLived()) {
    // Exchanges a short-lived access token for a long-lived one
    try {
      $accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
      echo "<p>Error getting long-lived access token: " . $helper->getMessage() . "</p>\n\n";
      exit;
    }
  }
  
  $_SESSION['fb_access_token'] = (string) $accessToken;

  $fb->setDefaultAccessToken($accessToken);
  $response = $fb->get('/me?locale=en_US&fields=id,name,email');
  $userNode = $response->getGraphUser();
  $email = $userNode->getField('email');
  $fb_user_id = $userNode->getField('id');

  $userVeridator = new UserVeridator();
  $userVeridator->isEmailDuplicate($email);
  $error = $userVeridator->getErrorArray();
  
  if(count($error) == 0)
  {
    // 沒有註冊
    $table = 'members';
    $data_array = array(
      'username' => $fb_user_id,
      'password' => '',
      'email' => $email,
      'active' => 'Yes',
      'fb_user_id' => $fb_user_id
    );
    Database::get()->insert($table, $data_array);
    
    $_SESSION['memberID'] = Database::get()->getLastId();
    $_SESSION['username'] = $fb_user_id;

  }else{
    // 有註冊了
    if($userVeridator->fbLoginVerification($email, $fb_user_id)){
      $_SESSION['memberID'] = $userVeridator->getMemberIdByFb($fb_user_id);
      $_SESSION['username'] = $fb_user_id;
    }
  }
  header('Location: home');
  exit;

另外我們需要這兩支新的 function 來驗證

UserVeridator.php

/**
     * 驗證臉書帳號是否已存在於資料庫中
     */
    public function fbLoginVerification($email, $fb_user_id) {
        $result = Database::get()->execute('SELECT memberID FROM members WHERE email = :email AND fb_user_id = :fb_user_id', 
        array(':email' => $email, ':fb_user_id' => $fb_user_id));
        if(isset($result[0]['memberID']) AND !empty($result[0]['memberID'])){
            return true;
        }
		return false;
    }

    /**
     * 用臉書 ID 取得 memberID
     */
    public function getMemberIdByFb($fb_user_id) {
        $result = Database::get()->execute('SELECT memberID FROM members WHERE fb_user_id = :fb_user_id', 
        array(':fb_user_id' => $fb_user_id));
        if(isset($result[0]['memberID']) AND !empty($result[0]['memberID'])){
            return $result[0]['memberID'];
        }
		return false;
    }

把這個臉書登入按鈕抽出來放這裡 當成一個介面的元件

view/_component/fb-login.php

<?php
$helper = $fb->getRedirectLoginHelper();
$loginUrl = $helper->getLoginUrl(Config::FB_CALLBACK_URL, array('email'));
echo '<a href="' . htmlspecialchars($loginUrl) . '">Log in with Facebook!</a>';

使用這個介面元件,放到我們原本 login 頁面

<div class="container">

	<div class="row">

	    <div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-3">
			<form role="form" method="post" action="<?=Config::BASE_URL?>do_login" autocomplete="off">
				<h2>Please Login</h2>
				<p><a href='register'>Register a New Account</a></p>
				<hr>

				<?php if ($msg->hasMessages()) $msg->display(); ?>

				<div class="form-group">
					<input type="text" name="username" id="username" class="form-control input-lg" placeholder="User Name" value="<?php if($msg->hasErrors()){ echo htmlspecialchars($_POST['username'], ENT_QUOTES); } ?>" tabindex="1">
				</div>

				<div class="form-group">
					<input type="password" name="password" id="password" class="form-control input-lg" placeholder="Password" tabindex="3">
				</div>
				
				<div class="row">
					<div class="col-xs-9 col-sm-9 col-md-9">
						 <a href='forget'>Forgot your Password?</a>
					</div>
				</div>
				
				<hr>
				<div class="row">
					<div class="col-xs-6 col-md-6"><input type="submit" name="submit" value="Login" class="btn btn-primary btn-block btn-lg" tabindex="5"></div>
					<?php include("view/_component/fb-login.php");?>
				</div>
			</form>
		</div>
	</div>
</div>

看起來就會這樣:
https://ithelp.ithome.com.tw/upload/images/20180111/20107394jbRkHYBHZB.png

按下去之後,自動註冊,若已有帳號就自動登入囉。
因為是臉書已經驗證過的信箱,所以我們就相信他的 email 直接免驗證。

require 通常用在 function 或 class ,include 則是靜態
include 的差異是 require 出錯是會顯示錯誤,會立刻停止不再執行
另外只有 include 可以用在迴圈,require 無法


上一篇
Day 24. PHP教學: 實作 FACEBOOK 臉書登入
下一篇
Day 26. PHP教學: 串接綠界 ECpay 線上支付
系列文
寫給朋友的 PHP 從 0 到 100 實戰教程30

尚未有邦友留言

立即登入留言