# Laravel 8 JWT ###### tags: `laravel`,`jwt` ## 參考資料 https://stackoverflow.com/questions/49650422/handle-jwt-auth-errors-in-laravel https://vocus.cc/article/5fa66df2fd897800012414c6 https://www.medianova.com/en-blog/2020/01/17/laravel-api-jwt-examples https://www.positronx.io/laravel-jwt-authentication-tutorial-user-login-signup-api/ ## 安裝 ```c! composer require tymon/jwt-auth "1.0.1" ``` 執行package publish: ```c! php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider" ``` 若顯示not found error: ```c! composer update ``` 執行完後再試一次 執行指令完畢後產生jwt.php檔案 產生金鑰 ```c= php artisan jwt:secret ``` 更新User Model User.php ```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 Tymon\JWTAuth\Contracts\JWTSubject; // 重要!!!-->implements JWTSubject class User extends Authenticatable implements JWTSubject { use HasFactory, Notifiable; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; /** * The attributes that should be cast to native types. * * @var array */ protected $casts = [ 'email_verified_at' => 'datetime', ]; // 新增以下程式碼 public function getJWTIdentifier() { return $this->getKey(); } public function getJWTCustomClaims() { return []; } } ``` 修改config/auth.php auth.php ```php= 'defaults' => [ // 'guard' => 'web', 'guard' => 'api', //<---修改此行 'passwords' => 'users', ], //... 'api' => [ 'driver' => 'jwt',//<---修改此行 // 'driver' => 'token', 'provider' => 'users', 'hash' => false, ], ], ``` 修改routes api.php ```php= //... Route::group([ 'middleware' => 'api', 'prefix' => 'user' ], function ($router) { Route::post('login', [AuthController::class, 'login']); Route::post('logout', [AuthController::class, 'logout']); Route::post('refresh', [AuthController::class, 'refresh']); Route::post('me', [AuthController::class, 'me']); }); ``` 修改app.php ```php= //... 'aliases' => [ //... 'JWTAuth' => 'TymonJWTAuthFacadesJWTAuth', 'JWTFactory' => 'TymonJWTAuthFacadesJWTFactory', ], ``` 若要使用httponly cookie, 修改config/cors.php supports_credentials改成true ```php! ... 'supports_credentials' => true, ``` 建立AuthController ```c= $ php artisan make:controller AuthController ``` AuthController.php https://gist.github.com/snailsmall612/eef2082a9fae6a5943bee127eacc12cc ```php= <?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Auth; use App\Http\Controllers\Controller; class AuthController extends Controller { /** * Create a new AuthController instance. * * @return void */ public function __construct() { $this->middleware('auth:api', ['except' => ['login']]); } /** * Get a JWT via given credentials. * * @return \Illuminate\Http\JsonResponse */ public function login() { use Illuminate\Support\Facades\Auth; ... //這段是讓用戶可以輸入email或name //前端input欄位name屬性要改成'name' $loginField = request('name'); $loginType = filter_var($loginField, FILTER_VALIDATE_EMAIL) ? 'email' : 'name'; request()->merge([$loginType => $loginField]); $credentials = [$loginType=>request('name'), 'password'=>request('password')]; //單純想讓用戶輸入email就用以下這行 // $credentials = request(['email', 'password']); if (! $token = auth()->attempt($credentials)) { return response()->json(['error' => 'Unauthorized'], 401); } //單純想回傳token就用以下這行 //return $this->respondWithToken($token); //使用httponly cookie的寫法: $user = Auth::user(); return response()->json($user->makeHidden([ "email_verified_at", "created_at", "updated_at"])) ->withCookie( 'jwt', //$name $token, //$value 3600, //$minutes null, //$path null, //$domain true, //$secure true, //$httpOnly false, //$raw 'none' //$sameSite ); } /** * Get the authenticated User. * * @return \Illuminate\Http\JsonResponse */ public function me() { return response()->json(auth()->user()); } /** * Log the user out (Invalidate the token). * * @return \Illuminate\Http\JsonResponse */ public function logout() { auth()->logout(); return response()->json(['message' => 'Successfully logged out']); } /** * Refresh a token. * * @return \Illuminate\Http\JsonResponse */ public function refresh() { return $this->respondWithToken(auth()->refresh()); } /** * Get the token array structure. * * @param string $token * * @return \Illuminate\Http\JsonResponse */ protected function respondWithToken($token) { return response()->json([ 'access_token' => $token, 'token_type' => 'bearer', 'expires_in' => auth()->factory()->getTTL() * 60 ]); } } ``` 其他注意事項: db相關設定要設定好(修改.env) 記得要執行migration jwt使用者的table名稱必須是users(檢查資料表) ### 其他 #### 設定用戶註冊後立即登入 https://dev.to/tngeene/handling-user-registration-and-authentication-on-a-laravel-api-using-jwt-4751 ```php! $token = auth()->login($user); ``` ## FAQ https://stackoverflow.com/questions/29303783/route-login-not-defined ### 測試API時出現錯誤訊息:Route [login] not defined 前端request的header加上: "Accept":"application/json" https://github.com/tymondesigns/jwt-auth/issues/1729 ### Argument 3 passed to Lcobucci\JWT\Signer\Hmac::doVerify() must be an instance of Lcobucci\JWT\Signer\Key, null given 解法如下: php artisan jwt:secret php artisan config:clear php artisan config:cache