資料庫設定
使用 make:migration
建立新的 migration:
1
php artisan make:migration --table= users add_api_token
Copy 加入 api_token
欄位:
1
2
3
4
5
6
7
8
9
public function up ()
{
Schema :: table ( 'users' , function ( Blueprint $table ) {
$table -> string ( 'api_token' , 80 ) -> after ( 'password' )
-> unique ()
-> nullable ()
-> default ( null );
});
}
Copy 執行遷移:
產生 token
官方的範例 是在使用者註冊時同時產生 token,但如果沒有另外實作更新機制的話可能不是很安全,這邊選擇的實作方式是在使用者登入時重新產生一組 token,這樣使用者不用自己管理 token,也不會永遠使用同一組 token。
使用 make:listener
建立一個 listener:
1
php artisan make:listener SuccessfulLogin
Copy 修改 app/Listeners/SuccessfulLogin.php
,加入以下內容,讓事件觸發時更新 API token。
1
2
3
4
5
6
public function handle ()
{
$user = Auth :: user ();
$user -> api_token = Str :: random ( 80 );
$user -> save ();
}
Copy 最後修改 app/Providers/EventServiceProvider.php
,將我們剛剛新增的 listener 綁定到使用者登入的事件:
1
2
3
4
5
6
7
protected $listen = [
// ...
'Illuminate\Auth\Events\Login' => [
'App\Listeners\SuccessfulLogin' ,
],
// ...
];
Copy
Illuminate\Auth\Events\Login
是 Laravel 內建的事件類型。
這時候進行登入,可以發現 api_token
在登入後會自動更新。
更新路由
只要對指定的路由增加 auth:api
的 middleware,即可讓 Laravel 收到 HTTP request 時自動驗證使用者端的 token。
routes/api.php
1
2
3
Route :: middleware ( 'auth:api' ) -> get ( '/user' , function ( Request $request ) {
return $request -> user ();
});
Copy 群組路由也行:
1
2
3
4
5
Route :: middleware ( 'auth:api' ) -> group ( function () {
Route :: get ( '/user' , function ( Request $request ) {
return $request -> user ();
});
}
Copy 設定 AJAX request 時自動加入 API token
預設情況下,只要在將 API token 塞進 query string 或 HTML form 就可以正常運作,可以參考官方的範例 。
但我自己比較喜歡將 token 放到 HTTP request header,而另一種使用方式為 Bearer Token,實作方式是將 Bearer
這個關鍵字與 API token 一起放進 Authorization
這組 HTTP request header,並且以一個空白字元做為區隔,長得像這樣子:
1
2
3
4
5
6
$response = $client -> request ( 'POST' , '/api/user' , [
'headers' => [
'Authorization' => 'Bearer ' . $token ,
'Accept' => 'application/json' ,
],
]);
Copy 這裡使用 axios 作為範例。首先,將 API token 以 HTML meta 的形式置入模板的 head 區塊
1
2
< meta name = "api-token" content = "{{ Auth::user()->api_token }}" >
< meta name = "csrf-token" content = "{{ csrf_token() }}" >
Copy 修改 resources/js/bootstrap.js
,在 CSRF token 這段,Laravel 預設是長這樣子:
1
2
3
4
5
6
7
let token = document . head . querySelector ( 'meta[name="csrf-token"]' );
if ( token ) {
window . axios . defaults . headers . common [ 'X-CSRF-TOKEN' ] = token . content ;
} else {
console . error ( 'CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token' );
}
Copy 仿照一樣的形式,加入 API token,並將 CSRF token 的變數名稱修改為 $csrfToken
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let apiToken = document . head . querySelector ( 'meta[name="api-token"]' );
let csrfToken = document . head . querySelector ( 'meta[name="csrf-token"]' );
if ( apiToken ) {
window . axios . defaults . headers . common [ 'Authorization' ] = `Bearer ${ apiToken . content } ` ;
} else {
console . error ( 'API token not found.' );
}
if ( csrfToken ) {
window . axios . defaults . headers . common [ 'X-CSRF-TOKEN' ] = csrfToken . content ;
} else {
console . error ( 'CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token' );
}
Copy 最後使用 npm
重新編譯,全部就大功告成了。
觀察 HTTP request header,API token 已被塞進 Authorization
一併發送給後端:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Host: localhost:8000
Connection: keep-alive
Accept: application/json, text/plain, */*
DNT: 1
X-XSRF-TOKEN: ed5uK2VqekJMaWxuVXoRVlIjoib2M4zVL01iTEZ4TU1ISlZjYFqVHJclwvZkxpT1wvTTBNI6IjlmODVmZIyZDU4NjZjZTY1Y2ZkYjFiZTFjNlYTVhNmE3Zjc3ZDifQ==
X-CSRF-TOKEN: L2Y5uUsKlz0JBQO6t4fNcUWw9dfRKNkGGhqVBRrW
X-Requested-With: XMLHttpRequest
Authorization: Bearer EjzimAAL6is1lgZZhczQGOdDbtjQyRaJTphPW2jj7cVhDhva8GhJNsU5GI94bkQKWqYLG9vjkXKstKfs
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:8000/check-benchmark
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: jweToken=%7B%22protected%22%3A%22eyJhbGciOiJSU0EiOiJBMjU2R0NNIn0%22%2C%22aad%22%3A%22eyJleHAiOiIyMDIwLTtMTdUMDY6NDU6MjVaIn0%22%2C%22encrypteEV_HYsAkuV5blzfi5CQ8VPbhvz_83xI1jokmRMOqSlQUcYsMfxjJkUkBu-HEGwY76qDrfzxxbBf5qj3ZN1okrBx6KCNq183SJ9hjQbdg9BYx2POPwDC1OjiaX0QiONJCFiMZVVeNdYPn7obBs%22%2C%22tag%22%3A%22JJ7EWL54bTIdR0Y6tgxyhg%2
Copy
References
API Authentication - Laravel - The PHP Framework For Web Artisans
Laravel 自带的 API 守卫驱动 token 使用详解 | Laravel China 社区