【大神勿進菜鳥參考】Laravel6 passport API 一個使用者只有唯一 token

aidybnt發表於2019-10-27

和我一樣的菜鳥可以參考參考哈。

一開始用的原生的API,水平有線,用的自己都不放心,後來思來想去就用passport試試吧。
折騰到感覺OK了,然後就去看資料庫,不看不知道,一看嚇一跳。
我有強迫症那麼多冗餘資料,如果訪問量大了起來,好可怕。
我就想每個使用者只有也僅有自己的對應TOKEN資料,其他的我不要。
於是就折騰出了以下的方式來實現。

app\Providers\AuthServiceProvider.php

密碼授權模式

public function boot()
    {
        $this->registerPolicies();
        Passport::routes(); //介面認證路由
        //這裡除錯的時候可以按分鐘設定,這三個一定要一直,否則會出現token過期時間不統一的情況,還有就是明明過期了,居然還能用
        //對於強迫症來說,不可接受
        Passport::tokensExpireIn(now()->addYear(1));
        Passport::refreshTokensExpireIn(now()->addYear(1));
        Passport::personalAccessTokensExpireIn(now()->addYear(1));
    }

其他的配置,按照官方的預設的來就可以。

【大神勿進 僅供菜鳥參考】Laravel6 passportAPI認證 禁止產生無效資料 一個使用者只有一個token

<?php

namespace App\Http\Controllers;

use App\Http\Requests\LoginUserRequest;
use App\Http\Requests\RegisterUserRequest;
use App\Model\User;
use GuzzleHttp\Client;
use Hash;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class UserController extends Controller
{
    public $token;

    public function register(RegisterUserRequest $request)
    {
        User::create([
            'name' => $request['name'],
            'email' => $request['email'],
            'password' => Hash::make($request['password'])
        ]);
        return response([
            'message' => '註冊成功',
        ]);
    }

    public function login(LoginUserRequest $request)
    {
        //適合全新的專案,如果已經有相關陣列存在,個人覺得可以挑選最大日期的可用token來加以處理
        //這樣做的好處可以確保使用者token的唯一性,不會再表裡存在大量無用資料,而且web也可以用
        //主要構思就是確保user表裡的remember_token、oauth_access_tokens表的id、oauth_refresh_tokens表裡的access_token_id,這三個數值保持一致
        $user = User::where('email', $request->email)->first();  //通過email查詢使用者資訊
        $remember_token = $user->remember_token;  //去user表裡找remember_token
        $oauth_access_token = DB::table('oauth_access_tokens')->where('user_id', $user->id)->first();  //先用id把oauth_access_tokens表拿到資料
        $oauth_access_token_count = DB::table('oauth_access_tokens')->where('user_id', $user->id)->count();  //用id去查oauth_access_tokens表裡的id
        if ($user) {
            if (Hash::check($request->password, $user->password)) {  //比對密碼
                if (!$remember_token or !$oauth_access_token_count) {
                    $token = $this->getToken($request['email'], $request['password']); //獲取token
                    $oauth_access_token_id = DB::table('oauth_access_tokens')
                        ->where('user_id', $user->id)
                        ->first()
                        ->id;  //把oauth_access_tokens表裡的id拿到,並儲存到user表的remember_id裡
                    User::where('id', $user->id)
                        ->update(['remember_token' => $oauth_access_token_id]);
                    return response([
                        'message' => '登入成功',
                        'user' => $user,
                        'token' => $token
                    ], 201);
                } elseif ($remember_token === $oauth_access_token->id) {
                    //比較兩個的值,如果是web使用者會直接關掉瀏覽器,在token過期的時候,也可以登入,但是讀取不了資料,401錯誤,授權已過期,這樣的話,提示登出再重新登入就可以了
                    return response([
                        'message' => '登入成功',
                        'user' => $user
                    ], 202);
                }
            } else {
                return response([
                    'message' => '密碼錯誤'
                ], 422);
            }
        } else {
            return response(['message' => '賬號不存在'], 422);
        }
    }

    public function logout(Request $request)
    {
        if (\Auth::guard('api')->check()) {  //檢測授權是否過期
            $user = \Auth::guard('api')->user();  //得到當前授權使用者的資訊
            User::where(['id' => $user->id])
                ->update(['remember_token' => '']);  //直接把user表裡的remember_token清空
            $token = $user->token();  //獲取當前使用者的token,對應的表為oauth_access_tokens
            $oauth_access_token_id = $token->id;
            DB::table('oauth_refresh_tokens')
                ->where(['access_token_id' => $oauth_access_token_id])
                ->delete(); //先刪除oauth_refresh_tokens表對應的資料
            $token->delete();  //然後刪除oauth_access_tokens表對應的資料
            return response([
                'message' => '授權已清除 已登出'
            ], 201);
        } else {
            $user_id = $request->id; //如果授權到期,先把使用者ID拿到
            $remember_token = User::find($user_id)->remember_token;
            //把三個對應的資料清空和刪除掉
            DB::table('oauth_access_tokens')
                ->delete($remember_token);
            DB::table('oauth_refresh_tokens')
                ->where(['access_token_id' => $remember_token])
                ->delete();
            User::where(['id' => $user_id])
                ->update(['remember_token' => '']);
            return response([
                'message' => '授權過期 授權已清除 已登出'
            ], 401);
        }
    }

    //獲取token
    public function getToken($email, $password)
    {
        $http = new Client();
        $response = $http->post(env('APP_URL') . 'oauth/token', [
            'form_params' => [
                'grant_type' => 'password',
                'client_id' => env('CLIENT_ID'),
                'client_secret' => env('CLIENT_SECRET'),
                'username' => $email,
                'password' => $password,
                'scope' => '*'
            ],
        ]);
        $this->token = json_decode((string)$response->getBody(), true);
        return $this->token;
    }
}

屎一坨的程式碼。

大概意思就是這樣。

如果已經一些資料。

我感覺可以這樣:

去查對應user_id,revoke = 0,日期排在最後的資料,刪除刪除掉。

有什麼好的建議,還請多多指教。

自用應該夠了。

<?php
Route::prefix('admin')->group(function () {
    Route::post('register', 'UserController@register');
    Route::post('login', 'UserController@login');
    Route::post('logout', 'UserController@logout');
    Route::post('refresh', 'UserController@refreshToken');
});
Route::middleware('auth:api')->group(function () {
    Route::prefix('hf')->group(function () {
        Route::post('show/{id}', 'HfController@show');
        Route::post('index', 'HfController@index');
    });
});

【大神勿進菜鳥參考】Laravel6 passport API 一個使用者只有唯一 token

首先設定環境變數,方便除錯,把token存入

【大神勿進菜鳥參考】Laravel6 passport API 一個使用者只有唯一 token

在點一次登入,就不在重新獲取token了,直接登入。(web使用者會有這樣的情況)

【大神勿進菜鳥參考】Laravel6 passport API 一個使用者只有唯一 token

測試獲取資料

【大神勿進菜鳥參考】Laravel6 passport API 一個使用者只有唯一 token

正常退出,環境變數和ID帶著

【大神勿進菜鳥參考】Laravel6 passport API 一個使用者只有唯一 token

未授權或授權過期狀態的退出
為了有區分,上下兩個提示是不一樣的。

【大神勿進菜鳥參考】Laravel6 passport API 一個使用者只有唯一 token

DONE

相關文章