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的介绍非常的详细,这次是透过之前使用过的经验加上网路上爬文分享实务上的经验给大家,希望对大家有帮助


<<:  DAY20 MongoDB Oplog 玩坏它

>>:  【Day5】情蒐阶段的小工具 ─ 线上免安装篇

踏上在AI时代追求人性之路(2):从设计背景出发

上一篇聊了从技术背景出发如何踏上追求人性之路, 这篇我想就我所知的部分, 聊聊如果是设计背景如何往这...

人脸辨识-day15 应用层面--1

人脸辨识的技术可使用两种方式搭建云端上或边缘装置上,那要如何选择将系统搭在哪个架构上,就要先了解系统...

Day 20 真真假假的 Instance doubles

该文章同步发布於:我的部落格 昨天有提到会稍微介绍一下 allow method,其实在昨天的范例...

gMSA 设定无密码的工作排程 (上 )

Group Managed ServiceAccounts (gMSA) 用途 AD网域的环境下,要...

JavaScript Day01 - 说明

前言 这次主要是更新我之前的笔记,那时候刚学习 JavaScript,对於一些结果可能不是很懂,刚好...