การทำ Authentication ใน Lumen + JWT
รู้จักกับ Lumen คืออะไร
Lumen คือ PHP Framework ที่ถูกพัฒนามาจาก Laravel โดยทำให้ Laravel Framework นั้นมีขนาดเล็กลงโดยการตัด Package บางตัวที่เกินความจำเป็นออก
Project Requirements
- PHP >= 7
- MySQL
- PDO PHP Extension
- Mbstring PHP Extension
- Composer >2.0.
ติดตั้ง LUMEN
ติดตั้งผ่าน Composer โดยใช้คำสั่ง
composer create-project --prefer-dist laravel/lumen lumen-jwt-auth
ในที่นี้จะตั้งชื่อ Project ว่า lumen-jwt-auth
เมื่อติดตั้งเสร็จแล้วทำการ Start server เพื่อดูหน้าตาของโปรเจ็คด้วยคำสั่งดังนี้
// เข้าไปที่ Folder project ที่สร้างขึ้น
cd lumen-jwt-auth
php -S localhost:8000 -t public
CONFIG PROJECT
1. GENERATE KEY
วิธีสร้าง Key นั้นมีหลายแบบ ในที่นี้จะใช้ \Illuminate\Support\Str::random(32);
โดยเข้าไปที่ ./routes/web.php แล้วเพิ่ม code
$router->get('/key', function () {
return response()->json([
'APP_KEY' => \Illuminate\Support\Str::random(32),
'JWT_SECRET' => \Illuminate\Support\Str::random(32)
]);
});
Start server แล้วไปที่ URL http://localhost:8000/key จะได้ข้อมูลดังนี้
{
"APP_KEY": "nqdMSGuZZGp7agnJpZE1jQaNhBDyvKE0",
"JWT_SECRET": "HRzDYZGQ04JEJfgMWy9UqnhfVyM2jqAMfneD04hffiMwE1hu7Nf5JPLJc1L4B0CD"
}
นำผลลัพธ์ที่ได้ไปแก้ไขที่ .env และเพิ่มค่า JWT_EXPIRE_HOUR (วันหมดอายุ Token หน่วยเป็น hour)
APP_KEY=qSPgQnFU50yfgBHNxLKz54t307nQ6z2x
. . .
JWT_SECRET=qSPgQnFU50yfgBHNxLKz54t307nQ6z2x
JWT_EXPIRE_HOUR=24
2. สร้างฐานข้อมูล
ให้ทำการสร้าง Database ใน MySQL แล้ว Config เพื่อเชื่อมต่อ
DB_PORT={DB_port}
DB_DATABASE={DB_name}
DB_USERNAME={DB_username}
DB_PASSWORD={DB_password}
3. ทำการสร้าง MIGRATION
php artisan make:migration create_users_table
แก้ไขไฟล์ .database/migrations/{timestamp}_create_users_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email', 100)->unique();
$table->string('password');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('users');
}
}
จากนั้น run คำสั่ง migration ใน cmd เพื่อสร้าง table ในฐานข้อมูล
php artisan migrate
แก้ไขไฟล์ .bootstrap/app.php
<?php
. . .
$app->withFacades(); // uncomment
$app->withEloquent(); // uncomment
4. สร้าง MODEL เพื่อติดต่อกับฐานข้อมูล
โดยสร้าง ไฟล์ที่ .app/Models/Users.php และเพิ่ม code ดังนี้
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Users extends Model
{
protected $table = 'users';
protected $fillable = [
'id', 'name', 'email',
];
protected $hidden = ['password'];
}
5. ติดตั้ง INSTALL JWT LIBRARY
Docs: https://github.com/firebase/php-jwt
โดยจะติดตั้ง firebase/php-jwt โดย run คำสั่งต่อไปนี้ ใน cmd
composer require firebase/php-jwt
6. สร้าง CONTROLLER
โดยสร้าง ไฟล์ที่ .app/Http/Controller/AuthController.php และเพิ่ม code ดังนี้
<?php
namespace App\Http\Controllers;
use App\Models\Users;
use Firebase\JWT\JWT;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class AuthController extends Controller
{
public function __construct(){}
/*
|--------------------------------------------------------------------------
| Api สมัครสมาชิก
|--------------------------------------------------------------------------
*/
public function register(Request $request)
{
// validator
$validator = Validator::make($request->all(), [
'email' => 'required|email|unique:users',
'username' => 'required|string|unique:users',
'password' => 'required',
'name' => 'required|string',
]);
if ($validator->fails()) {
$errors = $validator->errors();
return $this->responseRequestError($errors);
} else {
$user = new Users();
$user->email = $request->email;
$user->name = $request->name;
$user->username = $request->username;
$user->password = Hash::make($request->password);
if ($user->save()) {
$token = $this->jwt($user);
$user['api_token'] = $token;
return $this->responseRequestSuccess($user);
} else {
return $this->responseRequestError('Cannot Register');
}
}
}
/*
|--------------------------------------------------------------------------
| Api เข้าสู่ระบบ
|--------------------------------------------------------------------------
*/
public function login(Request $request)
{
$user = Users::where('username', $request->username)
->first();
if (!empty($user) && Hash::check($request->password, $user->password)) {
$token = $this->jwt($user);
$user["api_token"] = $token;
return $this->responseRequestSuccess($user);
} else {
return $this->responseRequestError("Username or password is incorrect");
}
}
/*
|--------------------------------------------------------------------------
| ตัวเข้ารหัส JWT
|--------------------------------------------------------------------------
*/
protected function jwt($user)
{
$payload = [
'iss' => "lumen-jwt", // Issuer of the token
'sub' => $user->id, // Subject of the token
'iat' => time(), // Time when JWT was issued.
'exp' => time() + env('JWT_EXPIRE_HOUR') * 60 * 60, // Expiration time
];
return JWT::encode($payload, env('JWT_SECRET'), 'HS256');
}
/*
|--------------------------------------------------------------------------
| response เมื่อข้อมูลส่งถูกต้อง
|--------------------------------------------------------------------------
*/
protected function responseRequestSuccess($ret)
{
return response()->json(['status' => 'success', 'data' => $ret], 200)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
}
/*
|--------------------------------------------------------------------------
| response เมื่อข้อมูลมีการผิดพลาด
|--------------------------------------------------------------------------
*/
protected function responseRequestError($message = 'Bad request', $statusCode = 200)
{
return response()->json(['status' => 'error', 'error' => $message], $statusCode)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
}
}
สิ่งที่เรานำมาใช้ใน Project
use Firebase\JWT\JWT; // เรียกใช้ JWT เพื่อนำมาสร้าง Token
use App\Models\Users; // เรียกใช้ Models เพื่อติดต่อกับ Table Users ในฐานข้อมูล
use Illuminate\Http\Request; // ใช้รับค่าที่ส่งมา
use Illuminate\Support\Facades\Hash; // ใช้เข้ารหัสข้อมูล เช่น password
use Illuminate\Support\Facades\Validator; // ใช้ตรวจสอบความถูกต้องและครบถ้วนของข้อมูล
7. สร้าง Route เพื่อเรียกใช้ Class ใน Controller
เพิ่ม Code ดังนี้
$router->group(['prefix' => 'api/auth'], function ($router) {
// URL: http://localhost:8000/api/auth/register, method: Post
$router->post('register', 'AuthController@register');
// URL: http://localhost:8000/api/auth/login, method: Post
$router->post('login', ['uses' => 'AuthController@login']);
});
7.1 ใช้ Postman ทดสอบที่ URL http://localhost:8000/api/auth/register ดังรูป
7.2 ใช้ Postman ทดสอบที่ URL http://localhost:8000/api/auth/login ดังรูป
8. สร้าง MIDDLEWARE เพื่อใช้ป้องกัน ROUTE
สร้าง file .app/Http/Middleware/JwtMiddleware.php และเพิ่ม code ดังนี้
<?php
namespace App\Http\Middleware;
use App\Models\Users;
use Closure;
use Exception;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class JwtMiddleware
{
public function handle($request, Closure $next, $guard = null)
{
$token = $request->header('Authorization');
if (!$token) {
// Unauthorized response if token not there
return response()->json([
'error' => 'Token not provided.',
], 401);
}
try {
$credentials = JWT::decode($token, new Key(env('JWT_SECRET'), 'HS256'));
} catch (ExpiredException $e) {
return response()->json([
'error' => 'Provided token is expired.',
], 400);
} catch (Exception $e) {
return response()->json([
'error' => 'An error while decoding token.',
], 400);
}
$user = Users::find($credentials->sub);
// Now let's put the user in the request class so that you can grab it from there
$request->auth = $user;
return $next($request);
}
}
9. เปิดใช้งาน MIDDLEWARE
ที่ .bootstrap/app.php โดยการเพิ่ม code
$app->routeMiddleware([
'jwt.auth' => App\Http\Middleware\JwtMiddleware::class,
]);
10. เพิ่ม MIDDLEWARE ใน ROUTE
ทำการเพิ่ม Middleware ใน URL ที่เราต้องการให้ login ก่อนเข้าใช้งาน
$router->group(['prefix' => 'api/', 'middleware' => 'jwt.auth'], function ($router) {
// URL: http://localhost:8000/api/'test', method: get
$router->get('test', function () {
return 'Hi admin';
});
});
ทำการทดสอบโดยใช้ Postman ดังภาพ โดยเพิ่ม Header ชื่อ Authorization โดยที่ value คือ token ที่ return กลับมาในข้อที่ 7.2
เพียงเท่านี้ก็เป็นอันเสร็จสิ้น
************************************************ จบแล้ว ************************************************
แนะนำ
บทความใหม่