iT邦幫忙

2021 iThome 鐵人賽

DAY 5
0
Software Development

全端開發包括測試自己一條龍!系列 第 5

Day 5 - 使用JWT Token幫Laravel 8.0做Authentication

Introduce

為了API的安全性,本次跟各位介紹透過JWT Token來幫API做身分驗證,簡單來說就是先讓使用者登入來取得Token,接下來需使用得到的Token進行其他API的訪問,並可以在程式當中透過Token來判別目前的使用者是誰.

  1. 安裝Jwt auth
$ sail composer require tymon/jwt-auth 1.*@rc
  1. 在config底下新增jwt.php配置文件
$ sail artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
  1. 產生加密密鑰
$ sail artisan jwt:secret
  1. 調整User model
<?php

namespace App\Models;

use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Models\Post;

class User extends Authenticatable implements JWTSubject
{
    /**
     * 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 [];
    }
}
  1. config/app.php內註冊兩個Facade,為了是方便使用JWTAuth和JWTFactory的功能
'aliases' => [
    ...
    'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth',
    'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory',
],
  1. 修改config/auth.php
'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],

...

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],
  1. 設定routes/api.php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;

Route::group(['prefix' => 'auth'], function () {
    Route::post('login', [AuthController::class, 'login']);
    Route::post('register', [AuthController::class, 'register']);
});
  1. 建立Controller, Service, Reposory
$ sail artisan make:controller AuthController
$ sail artisan make:service AuthService
$ sail artisan make:repository UserRepository

AuthController

  1. construct部分排除login, register,不需驗證token
<?php

namespace App\Http\Controllers;

use App\Services\AuthService;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Facades\JWTAuth;

class AuthController extends Controller
{
    protected $service;

    public function __construct(AuthService $service)
    {
        $this->middleware('jwt.auth', ['except' => ['login', 'register']]);
        $this->service = $service;
    }

    /**
     * 登入取得Token
     * @param Request $request
     * @return JsonResponse
     */
    public function login(Request $request)
    {
        $account = $request->input('account');
        $password = $request->input('password');
        $token = $this->service->login($account, $password);

        if ($token !== false) {
            $result = [
                'token_type' => 'Bearer',
                'access_token' => $token,
                'expires_in' => auth('api')->factory()->getTTL() * 60
            ];
        }

        return response()->json($result);
    }
    
    /**
     * 註冊使用者
     * @param Request $request
     * @return JsonResponse
     */
    public function register(Request $request)
    {
        $result = $this->service->register($request->all());
        return response()->json($result);
    }
}

AuthService

  1. 在login function判斷password matched
  2. 此處使用JWTAuth::fromUser($user)方法建立token,但token的建立不只一種,本範例這是基於User model返回instance產生token,好處是之後可以透過$user = Auth::guard('api')->user()取得User model進行使用
<?php

namespace App\Services;

use App\Repositories\UserRepository;
use Tymon\JWTAuth\Facades\JWTAuth;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Hash;

class AuthService
{

    /**
     * @var UserRepository
     */
    protected $user_repository;

    public function __construct(UserRepository $user_repository)
    {
        $this->user_repository = $user_repository;
    }

    public function login(string $account, string $password)
    {
        $user = $this->user_repository->searchUserByAccount($account);

        // 密碼不符
        if (! Hash::check($password, $user->password)) {
            // TODO error handler
            dd('password not matched');
        }

        return JWTAuth::fromUser($user);
    }

    public function register(array $data)
    {
        $name = Arr::get($data, 'name');
        $account = Arr::get($data, 'account');
        $password = Arr::get($data, 'password');
        $user = $this->user_repository->registerAccount($name, $account, $password);

        return $user;
    }
}

UserRepository

<?php

namespace App\Repositories;

use Exception;
use App\Models\User;
use Illuminate\Support\Facades\Hash;

class UserRepository
{
    /**
     * 透過帳號搜尋特定使用者
     *
     * @param string $account 帳號
     * @return mixed
     */
    public function searchUserByAccount(string $account)
    {
        try {
            return User::select(['*'])
                ->where('account', $account)
                ->first();
        } catch (Exception $e) {
            dd($e);
        }
    }

    /**
     * 建立使用者
     *
     * @param string $name 姓名
     * @param string $account 帳號
     * @param string $password 密碼
     * @return mixed
     */
    public function registerAccount(string $name, string $account, string $password)
    {
        try {
            return User::create([
                'name' => $name,
                'account' => $account,
                'password' => Hash::make($password),
            ]);
        } catch (Exception $e) {
            dd($e);
        }
    }
}

設定完成後,如果之後建立的API希望透過token驗證,可以在route處加上Route::middleware(['jwt.auth'])如下

# /routes/api.php
Route::middleware(['jwt.auth'])->group(function () {
    Route::group(['prefix' => 'user'], function () {
        Route::get('/', [UserController::class, 'index']);
    });
    Route::group(['prefix' => 'post'], function () {
        Route::post('/', [PostController::class, 'create']);
        Route::get('/', [PostController::class, 'index']);
    });
});

接下來使用Postman進行測試

  1. 先註冊一個新的User
    POST /auth/register

  2. 登入取得Token
    POST /auth/login

  3. 當API需要Authentication時,Header處加上Authorization

以上就是這次Authorization的介紹,網路上有很多關於Jwt token的介紹非常的詳細,這次是透過之前使用過的經驗加上網路上爬文分享實務上的經驗給大家,希望對大家有幫助


上一篇
Day 4 - 介紹Laravel Eloquent ORM
下一篇
Day 6 - Laravel 8.0 如何快速建立API
系列文
全端開發包括測試自己一條龍!10

尚未有邦友留言

立即登入留言