Laravel 9 JSON Web Token (JWT) 身份驗證教學範例 for Web RESTful API
 
                    Laravel 9 搭配 php-open-source-saver/jwt-auth 套件實作完整的 RESTful Web API 登入認證令牌 JSON Web Token (JWT),包含使用者註冊、登入、以 JWT 認證使用 API、更新 JWT 及使用者登出時移除 JWT。
資料庫設定
請參考 Laravel 8 RESTful Web API 登入認證令牌 JSON Web Token (JWT)
安裝並設定 JWT
上述已遷移完資料庫資料表 users,接著安裝並設定 Laravel JWT 身份驗證套件,使用以下指令安裝:
composer require php-open-source-saver/jwt-auth使用以下指令將 JWT 設定文件從供應商複製到 confi/jwt.php:
php artisan vendor:publish --provider="PHPOpenSourceSaver\JWTAuth\Providers\LaravelServiceProvider"產成 JWT 令牌加密 (secret):
php artisan jwt:secretJWT 加密產生在 .env:
JWT_SECRET = xxxxxxxx設定 AuthGuard
需要對 Laravel 進行一些設定,才能使 JWT AuthGuard 支援應用程身份驗證。對此檔案進行修改 config/auth.php  (請保留原程式):
- 使用 API guard使用 JWTdriver並將 API 設置為guard默認 API。
- 現在可以使用 Laravel 的內建身份驗證機制來工作 jwt-auth。
<?php
'defaults' => [
        'guard' => 'api',
        'passwords' => 'users',
    ],
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
                'driver' => 'jwt',
                'provider' => 'users',
        ],
    ],修改 User 模型
為了使 PHPOpenSourceSaverJWTAuthContractsJWTSubject 在的 User 模型上實現,將使用兩種 Method:getJWTCustomClaims() and getJWTIdentifier()。
將檔案中的程式替換為以下內容 app/Models/User.php:
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
    use HasFactory, Notifiable;
    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];
    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];
    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
     /**
     * 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 [];
    }
}
建立 AuthController
新建一個控制器 (Controller) 來處理身份驗證過程的核心邏輯,指行指令來產生:
php artisan make:controller AuthController將檔案中的程式替換為以下內容 app/Http/Controllers/AuthController.php:
- constructor:我們在類中建立此函數,- controller以便我們可以使用其中的中間件來阻止對控制器中某些方法的未經身份驗證的訪問- auth:api。
- login:此方法使用電子郵件和密碼對用戶進行身份驗證。當用戶成功通過身份驗證時,- Authfacade方法將返回 JWT 令牌。檢索生成的令牌並將其作為 JSON 與用戶對像一起返回- attempt()。
- register:此方法創建用戶記錄並使用令牌生成登錄用戶- Authtoken。
- logout:該方法使用戶- Authtoken失效。
- refresh:該方法使用戶- Authtoken 失效並生成新的 token。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use App\Models\User;
class AuthController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth:api', ['except' => ['login','register']]);
    }
    public function login(Request $request)
    {
        $request->validate([
            'email' => 'required|string|email',
            'password' => 'required|string',
        ]);
        $credentials = $request->only('email', 'password');
        $token = Auth::attempt($credentials);
        if (!$token) {
            return response()->json([
                'status' => 'error',
                'message' => 'Unauthorized',
            ], 401);
        }
        $user = Auth::user();
        return response()->json([
                'status' => 'success',
                'user' => $user,
                'authorisation' => [
                    'token' => $token,
                    'type' => 'bearer',
                ]
            ]);
    }
    public function register(Request $request){
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:6',
        ]);
        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);
        $token = Auth::login($user);
        return response()->json([
            'status' => 'success',
            'message' => 'User created successfully',
            'user' => $user,
            'authorisation' => [
                'token' => $token,
                'type' => 'bearer',
                'expires_in' => auth()->factory()->getTTL() * 60    // JWT 有效時間/分鐘
            ]
        ]);
    }
    public function logout()
    {
        Auth::logout();
        return response()->json([
            'status' => 'success',
            'message' => 'Successfully logged out',
        ]);
    }
    public function refresh()
    {
        return response()->json([
            'status' => 'success',
            'user' => Auth::user(),
            'authorisation' => [
                'token' => Auth::refresh(),
                'type' => 'bearer',
            ]
        ]);
    }
}
已經完成了 JWT 身份驗證的設定了。
新增測試 Database API
新增 Database table
新增 EmployeeController:
php artisan make:controller Api/EmployeeController --api程式碼如下:
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class EmployeeController extends Controller
{
    /**
     * 要先通過 JWT 認證才可使用 API
     */
    public function __construct()
    {
        $this->middleware('auth:api');
    }
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $query = DB::connection('sms')
            ->table('employee')
            ->get();
        return [
            'data' => $query
        ];
    }
    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $id = DB::connection('sms')
            ->table('employee')
            ->insertGetId($request->all());
        return $this->show($id);
    }
    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        $query = DB::connection('sms')
            ->table('employee')
            ->where('id', $id)
            ->get();
        return [
            'data' => $query
        ];
    }
    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        // $query = DB::connection('sms')
        //     ->table('employee')
        //     ->where('id', $id)
        //     ->update($request->all());
    }
    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        DB::connection('sms')
            ->table('employee')
            ->where('id', $id)
            ->delete();
    }
}
API routes
修改 API routes routes/api.php:
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;
use App\Http\Controllers\Api\EmployeeController;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
// Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
//     return $request->user();
// });
Route::controller(AuthController::class)->group(function () {
    Route::post('login', 'login');          // 登入 API
    Route::post('register', 'register');    // 註冊 (取得 JWT)
    Route::post('logout', 'logout');        // 登出
    Route::post('refresh', 'refresh');      // 重新生成 JWT
});
Route::apiResource('employee', EmployeeController::class);
測試 JWT 應用程式
使用者註冊
使用者註冊,使用 POST 在 Body 傳送給伺服器的資訊。
- name。
- email。
- password

使用者登入
使用者登入,使用 POST 在 Body 傳送給伺服器的資訊。
Postman POST 使用者登入,回傳 JWT token 如下紅框,後續要請求的 API 都得發送這個 token,來讓 Laravel 確認您有權限能使用 API。

取得 API 資訊
未使用 JWT token

使用 JWT token

使用者重新生成 JWT token
更新 JWT token,使用 POST 而 Auth 認證類型須使用 Bearer Token,並將上述取得的 Token 複製並貼至右則綠框的 Token 欄位。
紅框為 API 回傳的新 Token,後續 API 認證就要使用這個新 Token 了。

使用者登出,移除 JWT token
使用者登出,移除 JWT token,使用 POST 而 Auth 認證類型須使用 Bearer Token,並將上述取得的 Token 複製並貼至右則的 Token 欄位。
顯示使用者已登出,因此 Tokin 已刪除無法使用了。

參考
 
                            本著作係採用創用 CC 姓名標示-相同方式分享 3.0 台灣 授權條款授權.
