JWT/LARAVEL token expired

2020-08-05 11:32发布

问题:

I developed an API and I have a problem with the expiration of the token, and I try to find ways to refresh the tokens sent by API and I found nothing This is my user mdoal:

<?php

namespace App;


use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements JWTSubject
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password','username','lastname','tel','tel',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
    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 [];
    }
}

and this is my loginController

<?php

namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
use App\User;
use JWTFactory;
use JWTAuth;
use Validator;


class APILoginController extends Controller
{
    //


    public function login( Request $request){
        $validator = Validator::make($request -> all(),[
         'email' => 'required|string|email|max:255',
         'password'=> 'required'
        ]);

        if ($validator -> fails()) {
            # code...
            return response()->json($validator->errors());

        }


        $credentials = $request->all('email','password');
        try{
            if (! $token = JWTAuth::attempt( $credentials) ) {
                # code...
                return response()->json( ['error'=> 'invalid username and password'],401);
            }
        }catch(JWTException $e){

          return response()->json( ['error'=> 'could not create token'],500);
        }

        $currentUser = Auth::user();

        return response()->json( ['user'=> $currentUser,'token'=>compact('token')],200);
        ///return response()->json( compact('token'));

    }

}

and I found in github a solution to creat a custom middleware,When the token is expired, the refreshed token is added to the response headers. The app just needs to search if the response has this, if so, update the saved token.

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;

class RedirectIfAuthenticated
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string|null  $guard
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        try
        {
            if (! $user = JWTAuth::parseToken()->authenticate() )
            {
                return response()->json([
                    'code'   => 101, // means auth error in the api,
                   'response' => null // nothing to show
                 ]);
            }
        }
        catch (TokenExpiredException $e)
        {
            // If the token is expired, then it will be refreshed and added to the headers
            try
            {
                $refreshed = JWTAuth::refresh(JWTAuth::getToken());
                $user = JWTAuth::setToken($refreshed)->toUser();
                header('Authorization: Bearer ' . $refreshed);
            }
            catch (JWTException $e)
            {
                return response()->json([
                    'code'   => 103, // means not refreshable
                   'response' => null // nothing to show
                 ]);
            }
        }
        catch (JWTException $e)
        {
            return response()->json([
                'code'   => 101 ,//, means auth error in the api,
                   'response' => null // nothing to show
            ]);
        }

        // Login the user instance for global usage
        Auth::login($user, false);

        return  $next($request);
    }
}

回答1:

So you are using normal PHP methods for setting headers inside a Laravel middleware, that ain't going to work.

You should checko this out: https://github.com/tymondesigns/jwt-auth/blob/develop/src/Http/Middleware/BaseMiddleware.php

https://github.com/tymondesigns/jwt-auth/blob/develop/src/Http/Middleware/RefreshToken.php

Basically, change:

header('Authorization: Bearer ' . $refreshed);

to

$response = $next($request);
$response->headers->set('Authorization', 'Bearer '.$token);

The thing is the following, this aproach won't work as expected because this kind of "AfterMiddleware" gets executed after the request has been throught your app. So:

  1. User makes request with expired token
  2. The app will return a 403 or 401 (can't remember) because of TokenExpired.
  3. The response however will contain a header with the new token
  4. You make the same request with the new token and it should work.