JWT คืออะไร บทความนี้ไม่ขอกล่าว แต่แนะนำบทความนี้ อธิบายได้ละเอียดเลย “JSON Web Token มาตรฐานใหม่ ในการทำ Authentication” บทความที่ผมจะเขียนนี้จะพาทำ workshop ง่ายๆ แทบเหมือนว่า copy code จาก web “JSON Web Token Authentication for Laravel & Lumen” นี้มาเลยก็ว่าได้
ติดตั้ง Dependencies และ Config Project
- ต้องทำการติดตั้ง dependency ที่ชื่อว่า tymon/jwt-auth ด้วยคำสั่ง
$composer require tymon/jwt-auth
Using version ^1.0 for tymon/jwt-auth
./composer.json has been updated
Running composer update tymon/jwt-auth
Loading composer repositories with package information
Updating dependencies
...
[32mPackage manifest generated successfully.[39m
2 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
2. รันคำสั่ง Publish the config เพื่อสร้างไฟล์ jwt.php config ไฟล์ ที่ config/jwt.php ให้อัตโนมัติเองหลังจากรันคำสั่ง
$php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
Copied File [\vendor\tymon\jwt-auth\config\config.php] To [\config\jwt.php]
Publishing complete.
3. รันคำสั่ง Generate secret key เพื่อ generate jwt key ในไฟล์ .env JWT_SECRET=? เพื่อเป็น key ตั้งต้นให้การ Generate Token ทำ authenticate token หลังรันคำสั่งตรวจสอบ secret key ที่ .env
$php artisan jwt:secret
jwt-auth secret [Wwt4F********************************************UNvQ14f] set successfully.
4. แก้ไข class User.php เพิ่ม implement JWTSubject
<?php
namespace App;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements JWTSubject
{
use Notifiable;
// Rest omitted for brevity
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* @return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* @return array
*/
public function getJWTCustomClaims()
{
return [];
}
}
5. แก้ไข gaurds driver ของ api จากเดิม passport ไปเป็น jwt
– driver => jwt (jwt auth)
– provider => users (model auth user)
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
...
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
...
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
6. create table users
CREATE TABLE `users` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`email_verified_at` timestamp NULL DEFAULT NULL,
`password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
7. เพิ่ม basic authenticate route ที่ไฟล์ routes/api.php
– ‘middleware’ => ‘api’ (ประตูดัก request , response ของ route ที่มี pattern: /api/*)
– ‘prefix’ => ‘auth’ (prefix url ex: /api/auth/login )
Route::group([
'middleware' => 'api',
'prefix' => 'auth'
], function ($router) {
Route::post('register', 'App\Http\Controllers\AuthController@register');
Route::post('login', 'App\Http\Controllers\AuthController@login');
Route::post('logout', 'App\Http\Controllers\AuthController@logout');
Route::post('refresh', 'App\Http\Controllers\AuthController@refresh');
Route::post('me', 'App\Http\Controllers\AuthController@me');
});
8. สร้าง AuthController สำหรับใส่ Auth Logic (login , logout , register , info)
$php artisan make:controller AuthController
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
class AuthController extends Controller
{
/**
* Create a new AuthController instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login', 'register']]);
}
public function register(Request $request)
{
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password),
]);
$token = auth()->login($user);
return $this->respondWithToken($token);
}
/**
* Get a JWT via given credentials.
*
* @return \Illuminate\Http\JsonResponse
*/
public function login()
{
$credentials = request(['email', 'password']);
//return response()->json($credentials);
if (!$token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
/**
* Get the authenticated User.
*
* @return \Illuminate\Http\JsonResponse
*/
public function me()
{
return response()->json(auth()->user());
}
/**
* Log the user out (Invalidate the token).
*
* @return \Illuminate\Http\JsonResponse
*/
public function logout()
{
auth()->logout();
return response()->json(['message' => 'Successfully logged out']);
}
/**
* Refresh a token.
*
* @return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
return $this->respondWithToken(auth()->refresh());
}
/**
* Get the token array structure.
*
* @param string $token
*
* @return \Illuminate\Http\JsonResponse
*/
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60
]);
}
}
9. ทดสอบระบบ
– [POST] http://127.0.0.1:8000/api/auth/register (ระบบจะสร้าง user ใน table users )
– [POST] http://127.0.0.1:8000/api/auth/login (ระบบเช็ค user ใน table users)
– [POST] http://127.0.0.1:8000/api/auth/me (ระบบแสดง user จาก bearer token )
– [POST] http://127.0.0.1:8000/api/auth/logout (ระบบ expired token ออกจากระบบ)
สรุปท้ายบทความ
JWT เป็นเพียงการ Encode Data ที่จะกอบไปด้วย 3 ส่วนหลัก ๆ คือ Headers (เข้ารหัสแบบไหนอยู่ (เช่น SHA256, RSA)) ,Payload เป็นข้อมูลจริง ,Signature เอาไว้เช็คกับ Payload เป็นเพียงมาตรฐานเปิด (RFC 7519) ที่ช่วยเข้ามาแก้ไขโดยมีข้อดีหลัก ๆ 2 ข้อ คือขนาดไม่ใหญ่มาก ,ลักษณะการเก็บข้อมูลในตัวเอง
หากผู้อ่านท่านใดสงสัยหรืออยากที่จะแนะนำบทความ สามารถคอมเม้นทิ้งท้ายไว้ได้เลยครับ ^^