Laravel5 : ทำ Facebook Login บน Laravel 5
บทความนี้ผมจะพามาทำ ล็อคอินด้วยโซเชียลอย่าง facebook บน Laravel 5 กันครับ เรื่องเริ่มจากผมได้เขียน Benznest wallet แล้วก็อยากจะทำล็อคอินด้วย facebook (เห็นคนอื่นเค้ามีก็อยากจะมีบ้าง) ซึ่งบทความนี้จะเหมาะกับคนที่พอจะเข้าใจ Laravel 5 มาบ้างแล้ว เพราะไม่ได้ลงรายละเอียดพื้นฐานมาก โดยผมจะสรุปความรู้ที่ได้ทำลงในบทความนี้ครับ
เป้าหมายของผมก็คือ ผมมีแบบล็อคอินเดิมของ Laravel อยู่แล้ว สามารถสมัครสมาชิกปกติ แต่จะเพิ่มล็อคอินเฟสบุ๊คเข้ามา โดยผู้ใช้สามารถใช้งานได้ทั้งสองวิธี กล่าวคือ ถ้าสมัครสมาชิกปกติก็ไปเพิ่มเฟสบุ๊คทีหลังได้ หรือถ้ากดล็อคอินด้วยเฟสบุ๊คแล้วอีเมลตรงกับสมาชิกที่มีอยู่เดิม ก็จะเอาบัญชีเดิมเพิ่มเฟสบุ๊คเข้าไป แล้วค่อยมีหน้าจัดการบัญชีอีกทีหนึ่ง แต่ถ้าไม่มีข้อมูลก็จะสมัครสมาชิกให้อัตโนมัติโดยเอาข้อมูลจากเฟสบุ๊คนั่นแหละ เย้ มาลุยกัน
เริ่มต้น
ก่อนอื่นเลยต้องไปสร้าง App ของ facebook ก่อนครับ
https://developers.facebook.com/apps/
กด New app จากนั้นก็เลือก เว็บไซต์ จากนั้นก็ใส่ชื่อแอปครับ แล้วก้ใส่รายละเอียดตามขั้นตอน
พอใส่รายละเอียดครบแล้ว ให้เข้ามาที่ My app แล้วเลือกแอปของเราครับ มาตั้งค่าก่อนที่ Settings ตรงนี้สำคัญครับ ให้ใส่ App Domain ลงไป ถ้าจะเทสบน localhost ก็ทำได้เหมือนกัน แต่ต้องไปกำหนดก่อน ว่าแอปเป็นแอปแบบทดสอบ แต่ในกรณีนี้ผมจะให้โฮสจริงไปเลย
ดาวน์โหลดไลบรารี่ผ่าน Composer
ขั้นตอนต่อมา เราต้องไปโหลดไลบรารี่ การล็อคอินมาผนวกกับเว็บ laravel ของเราครับ โดยไปที่โปรเจค Laravel ของเราแล้ว เปิดไฟล์ composer.json ขึ้นมา จากนั้นก็ใส่ dependencies ลงไปในส่วนของ require
"require": { "laravel/framework": "5.0.*", "laravel/socialite": "~2.0" },
เสร็จแล้วก็ ทำการ update project ซะโดยเปิด terminal cmd ขึ้นมา แล้วก็เข้ามาที่ path project พิมพ์ว่า
composer update
หรือเข้าไปที่โปรเจคผ่าน file explorer แล้วคลิกขวา เลือก Update composer พอเปิดขึ้นมาก็กด enter ทีนึง
จากนั้นก็รอโหลด (ต่อเน็ตด้วยนะ)
ใส่ไลบรารี่ลงใน Laravel
หลังจากที่เราโหลดไลบรารี่มาแล้ว ตัวไลบรารี่จะไปอยู่ในโฟลเดอร์ /vendor ครับ แต่โปรเจคจะยังไม่ได้ไลบรารี่นำมาใช้ ต้องนำเข้ามาในโปรเจคก่อน ผ่านตัว Provider วิธีการคือเข้าไปที่ไฟล์ config/app.php แล้วเพิ่มชื่อ Class ลงไปใน providers
Laravel\Socialite\SocialiteServiceProvider::class,
จะได้ประมาณนี้ครับ
ต่อมาเราก็ไปเพิ่มชื่อย่อเพื่อให้เราสามารถเรียกได้ง่ายขึ้นด้วย วิธีการก็คือ ไปที่ไฟล์ config/app.php ไฟล์เดิม แล้วเลื่อนลงมาด้านล่าง ให้เพิ่มบรรทัดด้านล่างลงไปใน aliases
'Socialize' => Laravel\Socialite\Facades\Socialite::class
เท่านี้ โปรเจคของเราก็จะโหลดตัวคลาสของไลบรารี่เข้าไปแล้ว ทำงานได้อย่างสบายใจ ต่อมาเราต้องไปเพิ่มรายละเอียด service ที่ facebook ต้องใช้ โดยเข้าไปที่ config/service.php
'facebook' => [ 'client_id' => '1234', 'client_secret' => '1234', 'redirect' => 'http://www.benzneststudios.com/benznestwallet/auth/login/facebook/index', ],
ตรงนี้คือเราต้องกำหนด client_id และ client_secret โดยนำมาจาก app facebook ที่เราสร้างไว้ตอนแรกนั่นเอง ส่วน redirect คือ ลิงค์ที่เราต้องการให้วิ่งไป หากล็อคอินผ่านแล้ว
เขียนโปรแกรมเรียกใช้งาน
หลังจากที่เราเตรียมการจนพร้อมแล้วทั้ง facebook app , library ต่างๆ ตอนนี้เราต้องมาเขียนโค้ดเพื่อใช้งานกันแล้ว เริ่มจากเตรียม Controller และ Route ก่อน โดยเตรียมไว้ 2 route อันแรกคือสำหรับส่ง redirect ไปยัง facebook อันที่สองคือไว้สำหรับรอรับ call back จาก facebook ส่วน Controller ผมสร้างอันใหม่ชื่อว่า SocialLoginController
วิธีการสร้างใช้ terminal ก็ได้พิมพ์ว่า
php artisan make:controller SocialLoginController
หรือจะ copy code ตัว Controller เก่า มาแปะ แก้ไขใน new file ก็ได้ แล้วแต่สะดวก (ผมชอบทำวิธีนี้) ส่วน Route สามารถกำหนดได้ตามสะดวกเลยครับ ผมกำหนด ‘auth/login/facebook’ เป็น route สำหรับส่ง redirect ไปหา facebook โดยให้ไปเรียก method ที่ชื่อ facebookAuthRedirect
ส่วน ‘auth/login/facebook/index’ ก็ไปเรียก facebookSuccess
ไฟล์ route.php
// for redirect to facebook auth. Route::get('auth/login/facebook', 'SocialLoginController@facebookAuthRedirect'); // facebook call back after login success. Route::get('auth/login/facebook/index', 'SocialLoginController@facebookSuccess');
ทีนี้ก็ไป implement ตัว method สำหรับ redirect ไปที่ facebook
ไฟล์ SocialLoginController.php
ก่อนอื่น import ตัว class เข้ามาเพิ่มก่อนที่ด้านบนของไฟล์
use Socialize;
public function facebookAuthRedirect() { return Socialize::with('facebook')->redirect(); } );
มา implement ที่ method facebookSuccess กันบ้าง
public function facebookSuccess() { $provider = Socialize::with('facebook'); if (Input::has('code')){ $user = $provider->stateless()->user(); dd($user); // print value debug. } }
ทำการรับค่าจาก callback จาก facebook ก่อน แล้วเช็คว่าที่ส่งมา มีส่งมาจริงๆนะ แล้วก็ดึงข้อมูลส่วน user ออกมา ผมจะลอง print debug ก่อน โดยใช้ dd()
ให้ลองเข้าไปที่ route ที่เรากำหนดไว้ (ล็อคอินเฟสบุ๊คไว้ด้วย)
auth/login/facebook
หากถูกต้องมันจะวิ่งไปที่ facebook แล้วส่งข้อมูลกลับมาแบบนี้
หากมี error ที่เฟสบุ๊คก็ต้องไปแก้ ที่ Facebook App ของเราครับ ส่วนใหญ่น่าจะเป็นที่ App domain ต้องตั้งให้ตรงกับเว็บของเรา
พอได้รับผลกลับมาแล้ว ที่เหลือเราก็ดึงค่าออกมาจากตัวแปร user ก็จะได้พวก email, name, facebook_id (อย่าลืมไปสร้างฟิล facebook_id ในฐานข้อมูลด้วยนะ ถ้าอยากจะเก็บ)
$email = $user->email; $name = $user->name; $facebook_id = $user->id;
ทีนี้ต้องระวังนิดนึงด้วย เพราะผู้ใช้บางคน กำหนด privacy ไม่แสดง email ทำให้ค่าของ email อาจจะเป็น null ได้ เดี๋ยวใส่ลงไปในฐานข้อมูลจะพังได้ (ถ้าไปตั้งว่า email ต้อง unique) ดังนั้นเราต้องมาจัดการตรงนี้ด้วย วิธีการก็แค่เช็คว่า email เป็น null หรือไม่
if($email == null){ // case permission is not email public. $user = $this->checkExistUserByFacebookId($facebook_id); if($user == null){ $email = $facebook_id; } } else { $user = $this->checkExistUserByEmail($email); if($user != null){ if($user->facebook_id == ""){ // update account when not have facebook id. $user->facebook_id = $facebook_id; $user->save(); } } }
จากนั้นก็แล้วแต่จะจัดการครับ ขอยกตัวอย่างล็อคอินของผมที่ทำละกันครับ ถ้าระบบเราสามารถจะล็อคอินแบบปกติได้ด้วย ใช้อีเมล รหัสผ่าน (2 ช่องทาง) ก็ดูว่า facebook ส่งอีเมลผู้ใช้มาด้วยรึปล่าว ถ้ามีก็เอาอีเมลนี้แหละ แต่ถ้าไม่มี ก็อาจจะต้องหาอะไรมาใส่แทนอีเมลครับ เพราะไม่ควรว่างฟิลอีเมลไว้ในฐานข้อมูล ผมเลยใช้ facebook_id ใส่แทนอีเมลลงไป (ไม่มีซ้ำกัน) และอย่าลืมว่าผู้ใช้ที่กดเข้ามา อาจจะเป็นทั้งคนเก่า (สมาชิกเดิม) และคนใหม่ ดังนั้นต้องเอา facebook_id หรือ อีเมลไปหาในฐานข้อมูลก่อนว่าเคยมีคนนี้มัย ถ้ามีก็คือคนเก่าเราก็ล็อคอินเข้าไป แต่ถ้าไม่มีก็ทำการสมัครสมาชิกอัตโนมัติ
ฟังก์ชันสำหรับเช็คว่าเป็นสมาชิกเดิมหรือปล่าว ทำได้หลายวิธีครับ แต่ผมใช้ Eloquent ที่ผูกกับ Model ละกัน หากมีข้อมูลผู้ใช้อยู่แล้วมันก็จะ return object ของ user คนนั้นออกมาให้ แต่ถ้าไม่มีก็จะเป็น null
private function checkExistUserByEmail($email){ $user = \App\User::where('email','=',$email)->first(); return $user; } private function checkExistUserByFacebookId($facebook_id){ $user = \App\User::where('facebook_id','=',$facebook_id)->first(); return $user; }
พอได้ object ของ user มาก็ง่ายแล้วครับ มาเช็คดูว่า เป็น null หรือไม่ ถ้าไม่เป็นก็เรียกใช้ login ได้เลยโดยการโยน Model ให้มันไป แล้วจะ authen ให้
Auth::login($user);
หรือจะล็อคอินด้วย primary key ก็ได้ โดยเรียก loginUsingId($id)
Auth::loginUsingId($user_id);
แต่ถ้ากรณีเป็น null ก็สมัครสมาชิกก่อน โดยไปสร้าง User ใหม่ แล้วค่อยล็อคอิน หรือจะทำอะไรก็แล้วแต่ต้องการ ถ้าทุกอย่างเสร็จแล้วผมจะให้ redirect ไปหน้าแรก
if($user != null){ // Auth exist account. Auth::login($user); return redirect('index/'); } else{ // new Account. $user = $this->registerUser($email,$name,$password,$facebook_id); Auth::login($user); return redirect('index/'); }
ทำการเขียนฟังก์ชัน สมัครสมาชิก โดยผมทำผ่าน Model เลย ส่วนรหัสผ่านผู้ใช้ แน่นอนว่าก็ไม่ควรว่างไว้ แต่จะใส่อะไรดีละ จะ fix ไว้ก็คงไม่ดี แล้วผู้ใช้ก็ไม่จำเป็นต้องมากรอกรหัสผ่านด้วย ผมเลยใช้วิธีนำ token มาใส่แทนรหัสผ่านซะเลย (อาจจะตัดเอา 10 หรือ 20 ตัว) แล้วใส่ Hash
private function registerUser($email,$name,$password,$facebook_id){ $user = new \App\User; $user->email = $email; $user->name = $name; $user->password = Hash::make($password); // Hash::make $user->balance = 0; $user->level = "member"; $user->facebook_id = $facebook_id; $user->save(); return $user; }
สรุป SocialLoginController.php
<?php namespace App\Http\Controllers; use Input; use Socialize; use Auth; use Redirect; use Hash; class SocialLoginController extends Controller { public function __construct() { } public function index() { return view('home'); } public function facebookAuthRedirect() { return Socialize::with('facebook')->redirect(); } public function facebookSuccess() { $provider = Socialize::with('facebook'); if (Input::has('code')){ $user = $provider->stateless()->user(); //dd($user); $email = $user->email; $name = $user->name; $password = substr($user->token,0,10); $facebook_id = $user->id; if($email == null){ // case permission is not email public. $user = $this->checkExistUserByFacebookId($facebook_id); if($user == null){ $email = $facebook_id; } } else { $user = $this->checkExistUserByEmail($email); if($user != null){ if($user->facebook_id == ""){ // update account when not have facebook id. $user->facebook_id = $facebook_id; $user->save(); } } } if($user != null){ // Auth exist account. Auth::login($user); return redirect('index/'); } else{ // new Account. $user = $this->registerUser($email,$name,$password,$facebook_id); Auth::login($user); return redirect('index/'); } } return redirect('/'); } private function checkExistUserByEmail($email){ $user = \App\User::where('email','=',$email)->first(); return $user; } private function checkExistUserByFacebookId($facebook_id){ $user = \App\User::where('facebook_id','=',$facebook_id)->first(); return $user; } private function registerUser($email,$name,$password,$facebook_id){ $user = new \App\User; $user->email = $email; $user->name = $name; $user->password = Hash::make($password); // Hash::make $user->facebook_id = $facebook_id; $user->save(); return $user; } }
มาลองใช้งานกัน
วิธีใช้งานนั้นง่ายมาก ไปที่ View แล้วก็ทำปุ่มมาสักปุ่มนึง จากนั้นแปะลิงค์ให้มันไป แล้วก็เรียกใช้งาน route ที่ชื่อว่า /auth/login/facebook เป็นอันเสร็จพิธี
<a href="{{ url('auth/login/facebook') }}"><img src="{{ url('img/btn_facebook.png')}}" width="200px"> </a>
ส่วนกรณีที่ ผู้ใช้สมัครสมาชิกเข้ามาแต่ไม่ได้ให้ email มาด้วย (email เป็น null) จากโค้ดที่เราเขียนจะทำให้ email เป็น facebook_id ใช่มัยครับ แถมรหัสยังเป็น token อีก ดังนั้นผู้ใช้จะยังล็อคอินตรงๆไม่ได้ ดังนั้นอาจจะต้องทำหน้าตั้ง email รหัสผ่านให้เขาด้วยนั่นเอง อันนี้แล้วแต่สะดวกเลยครับ หรือจะเลือกไม่ให้สมัครสมาชิกตั้งแต่แรกเลยก็ได้ ตัดปัญหา ให้ reject ออกไปเลย (ไปเอาอีเมลมาก่อนสิ) อีกวิธีคือก็ให้เขาล็อคอินแค่เฟสบุ๊คนี่แหละ ไม่ต้องไปใช้ล็อคอินของเรา
สรุป
การทำล็อคอินเฟสบุ๊ค บน Laravel 5 นั้น ขั้นตอนหลัๆกคือ implement ส่วน controller ที่เหลือแค่สมัคร Facebook App โหลดไลบรารี่ เพิ่ม provider และ service นิดหน่อย ซึ่งระบบสมาชิก ในแต่ละที่นั้นต่างกัน อยู่ที่ระบบจัดการ บางที่ไม่ต้องการให้ล็อคอินได้หลายวิธี บางที่สามารถล็อคอินได้โดยตรงด้วย ตรงนี้ก็สามารถจัดการได้หลายแบบครับ โค้ดที่ผมเขียนเป็นตัวอย่างเท่านั้นครับ สามารถลองนำไปปรับใช้ ประยุกต์ให้เข้ากับระบบได้ครับ หวังว่าบทความนี้จะเป็นประโยชน์กับผู้ที่สนใจไม่มากก็น้อยครับ
ขอบคุณที่ติดตามครับ (: