PHP

Laravel ทำ JWT Authentication มีโค๊ดตัวอย่าง (Laravel 7.x) EP7

JWT คืออะไร บทความนี้ไม่ขอกล่าว แต่แนะนำบทความนี้ อธิบายได้ละเอียดเลย “JSON Web Token มาตรฐานใหม่ ในการทำ Authentication” บทความที่ผมจะเขียนนี้จะพาทำ workshop ง่ายๆ แทบเหมือนว่า copy code จาก web “JSON Web Token Authentication for Laravel & Lumen” นี้มาเลยก็ว่าได้

ติดตั้ง Dependencies และ Config Project

  1. ต้องทำการติดตั้ง 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\[email protected]');
    Route::post('login', 'App\Http\Controllers\[email protected]');
    Route::post('logout', 'App\Http\Controllers\[email protected]');
    Route::post('refresh', 'App\Http\Controllers\[email protected]');
    Route::post('me', 'App\Http\Controllers\[email protected]');

});

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 ออกจากระบบ)

http://127.0.0.1:8000/api/auth/register
http://127.0.0.1:8000/api/auth/login
http://127.0.0.1:8000/api/auth/me (Bearer Token ด้วย)
http://127.0.0.1:8000/api/auth/logout (Bearer Token ด้วย)

สรุปท้ายบทความ

JWT เป็นเพียงการ Encode Data ที่จะกอบไปด้วย 3 ส่วนหลัก ๆ คือ Headers (เข้ารหัสแบบไหนอยู่ (เช่น SHA256, RSA)) ,Payload เป็นข้อมูลจริง ,Signature เอาไว้เช็คกับ Payload เป็นเพียงมาตรฐานเปิด (RFC 7519) ที่ช่วยเข้ามาแก้ไขโดยมีข้อดีหลัก ๆ 2 ข้อ คือขนาดไม่ใหญ่มาก ,ลักษณะการเก็บข้อมูลในตัวเอง

หากผู้อ่านท่านใดสงสัยหรืออยากที่จะแนะนำบทความ สามารถคอมเม้นทิ้งท้ายไว้ได้เลยครับ ^^

Laravel เริ่มติดตั้งและสร้าง route ง่าย ๆ EP1

Laravel ปัจจุบันเดินทางมาถึงเวอร์ชั่น 8.X แล้ว ตั้งแต่เวอร์ชั่น 5 จนมาถึงปัจจุบันมีออะไรเปลี่ยนไปค่อนข้างเยอะ ที่เห็นได้ชัด ๆ เรื่องเวอร์ชั่นหลักของ PHP ที่เปลี่ยนจาก 5.X ไปเป็น 7.X เพิ่มความสามารถ ปรับปรุงเรื่องความปลอดภัยและ อื่น ๆ อีกมากมาย

เตรียมเครื่องมือ ทำความรู้จักเบื้องต้น

  • Composer เป็น PPM (PHP Packages management) จัดการ package ในภาษา PHP ส่วนของ Laravel จะให้ในการติดตั้ง global laravel/installers ศึกษาเพิ่มเติมที่นี่
    • composer global require laravel/installer
      • laravel new blog
    • composer create-project –prefer-dist laravel/laravel blog
  • VSCode เครื่องมือใช้สำหรับเขียนโค๊ด laravel ที่จะมี extensions ที่ช่วยเรื่องการเขียน Laravel ให้ดียิ่งขึ้น

เริ่มติดตั้งใช้งานเบื้องต้น

  1. ติดตั้ง composer ให้เรียบร้อย (ข้ามไป ไม่พูดถึงขั้นตอนนี้) จากนั้นตรวจสอบเวอร์ชั่น
    $ composer --version
    > Composer version 1.9.2 2020-01-14 16:30:31
    1. การติดตั้งแบบที่ 1 ติดตั้งด้วยการใช้ laravel cmd ที่ได้จากการติดตั้งจากขั้นตอนนี้
      $ composer global require laravel/installer
      $ laravel new blog
    2. การติดตั้งด้วย composer โดยตรง
      $ composer create-project --prefer-dist laravel/laravel blog
  2. ตัวอย่างใช้คำสั่ง
    $ laravel new poolsawat.com
    > Crafting application... Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Package operations: 106 installs, 0 updates, 0 removals - Installing doctrine/inflector (2.0.3): Loading from cache - Installing doctrine/lexer (1.2.1): Loading from cache - Installing dragonmantank/cron-expression (3.0.1): Downloading (100%) - Installing voku/portable-ascii (1.5.3): Loading from cache ... > @php -r "file_exists('.env') || copy('.env.example', '.env');" > @php artisan key:generate --ansi [32mApplication key set successfully.[39m > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: [32mfacade/ignition[39m Discovered Package: [32mfideloper/proxy[39m Discovered Package: [32mfruitcake/laravel-cors[39m Discovered Package: [32mlaravel/tinker[39m Discovered Package: [32mnesbot/carbon[39m Discovered Package: [32mnunomaduro/collision[39m [32mPackage manifest generated successfully.[39m Application ready! Build something amazing.
  3. ตรวจสอบโครงสร้างไฟล์ จะได้หน้าตาแบบนี้

4. ทำการสร้าง route “hello” ที่ไฟล์ route/web.php

5. รันคำสั่ง start server
$ php artisan serve
> Starting Laravel development server: http://127.0.0.1:8000

6. เช็คผลการสร้าง route “hello” ได้ที่ http://localhost:8000/hello

นี่คือแบบที่ง่ายที่สุด สำหรับมือใหม่ บทความต่อ ๆ ไปจะมาเรียนรู้เรื่องอะไรกัน คอยติดตามด้วยนะครับ