【问题标题】:Laravel-vue jwt token refreshLaravel-vue jwt 令牌刷新
【发布时间】:2019-03-23 21:30:18
【问题描述】:

我正在使用来自 Tymon 的 jwtAuth package 来处理从我的 laravel 后端到 vue spa 前端的身份验证,我正在创建 AuthController,我从文档中获取了几乎所有内容,并对其进行了一些调整以满足我的需要。从登录到注销以及令牌到期时,一切正常。

问题是我确实看到该控制器上有一个令牌刷新功能,如果我的猜测是正确的,它就是刷新客户端已经拥有的当前令牌。但是怎么做呢?如何在前端处理该刷新令牌?因为每 60 分钟(默认情况下令牌生命周期)非常烦人,所以它会抛出 401。

我想要的可能是每次用户向后端发出请求时,它都会刷新令牌或增加令牌的生命周期。因此,只有当用户空闲整整 60 分钟时,令牌才会过期。

我们可以这样做吗?这是最佳做法吗?我对整个jwt 和令牌的事情都很陌生,在过去,我只依赖 laravel 令牌过期,因为我不使用 spa,但是刀片前端所以大多不需要乱搞laravel 认证用户的方式。

这里的附加信息是我认为与整个身份验证有关的每个文件。

这是我的授权控制器

<?php

namespace App\Http\Controllers;

use DB;
use Hash;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Input;
use App\Http\Controllers\Controller;
use App\User;
use Response;

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()
    {
        $credentials = request(['username', 'password']);

        if (! $token = auth('api')->attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $this->respondWithToken($token);
    }

    /**
     * Get the authenticated User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function me()
    {
        return response()->json(auth('api')->user());
    }

    /**
     * Log the user out (Invalidate the token).
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        auth('api')->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    /**
     * Refresh a token.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        return $this->respondWithToken(auth('api')->refresh());
    }

    /**
     * Get the token array structure.
     *
     * @param  string $token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        $id = auth('api')->user()->getId();
        $kelas = User::with('pus','cu')->findOrFail($id);

        return response()->json([
            'access_token' => $token,
            'user' => $kelas,
            'token_type' => 'bearer',
            'expires_in' => auth('api')->factory()->getTTL() * 60
        ]);
    }

    public function guard()
    {
        return Auth::Guard('api');
    }

}

这是我的 API 路由

Route::group(['prefix' => 'auth'],function($router){
    Route::post('/login', 'AuthController@login');
    Route::post('/logout', 'AuthController@logout');
    Route::post('/refresh', 'AuthController@refresh');
    Route::get('/me', 'AuthController@me');
});

这是我的vue general.js 文件,它处理路由并将标头提供给 Axios

export function initialize(store, router) {
  router.beforeEach((to, from, next) => {
      const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
      const currentUser = store.state.auth.currentUser;

      if(requiresAuth && !currentUser) {
          next('/login');
      } else if(to.path == '/login' && currentUser) {
          next('/');
      } else {
          next();
      }
  });

  axios.interceptors.response.use(null, (error) => {
      if (error.response.status == 401) {
          store.dispatch('auth/logout');
          router.push('/login');
      }

      return Promise.reject(error);
  });

  if (store.state.auth.currentUser) {
      setAuthorization(store.state.auth.currentUser.token);
  }
}

export function setAuthorization(token) {
  axios.defaults.headers.common["Authorization"] = `Bearer ${token}`
}

这里是处理登录的auth.js

import { setAuthorization } from "./general";

export function login(credentials){
  return new Promise((res,rej) => {
    axios.post('/api/auth/login', credentials)
      .then((response) => {
        setAuthorization(response.data.access_token);
        res(response.data);
      })
      .catch((err) => {
        rej("Username atau password salah");
      })
  })
}

【问题讨论】:

    标签: laravel vue.js jwt


    【解决方案1】:

    您可以调整令牌到期时间(从 .envJWT_TTL)和刷新时间 (JWT_REFRESH_TTL) 以满足您的需求。并检查令牌是否有效和/或是否需要在中间件中刷新,以便令牌在需要时尽快刷新。

    关于这是否是一个好的做法,请参阅 Laravel 项目的 config/jwt.php 中的 cmets 到 JWT_REFRESH_TTL

    对我来说效果很好的解决方案是使用扩展 Tymon\JWTAuth\Http\Middleware\BaseMiddleware 的自定义中间件。样板文件看起来像这样:

    Class TryTokenRefresh extends BaseMiddleware
    {
        public function handle($request, Closure $next)
        {
            $newToken = $this->tryRefresh($request);
            if ($newToken) {
               // in case there's anything further to be done with the token
               // we want that code to have a valid one
               $request->headers->set('Authorization', 'Bearer ' . $newToken);
            }
    
            ...
            ...
            $response = $next($request);
            ...
            ...
    
            if ($newToken) {
                // send new token back to frontend
                $response->headers->set('Authorization', $newToken);
            }
    
            return $response;
        }
    
        // Refresh the token
        protected function tryRefresh()
        {
            try {
                $token = $this->auth->parseToken()->refresh();
                return $token;
            } catch (JWTException $e) {
                // token expired? force logout on frontend
                throw new AuthenticationException();
            }
    
            return null;
        }
    

    在前端,只需在响应中查找 Authorization 标头即可:

    // check for the `Authorization` header in each response - refresh on frontend if found
    axios.interceptors.response.use((response) => {
      let headers = response.headers
    
      // your 401 check here
      // token refresh - update client session
      if (headers.authorization !== undefined) {
       setAuthorization(headers.authorization);
      }
    
      return response
    })
    

    希望这会有所帮助。

    【讨论】:

    • 所以我需要在 api 路由上添加另一个中间件?我可以看看你的中间件的路线和实现吗?
    • 额外的中间件不是强制性的,但我强烈推荐它。您可以通过调整JWT_TTLJWT_REFRESH_TTL 开箱即用地实现您想要的。相关部分中间件代码如我上面发布的那样。路由将被定义为使用middleware =&gt; [ 'token.refresh' ]
    猜你喜欢
    • 2015-05-19
    • 1970-01-01
    • 2019-07-04
    • 2016-03-05
    • 2016-06-25
    • 2021-05-17
    • 2018-04-01
    • 1970-01-01
    • 2021-04-06
    相关资源
    最近更新 更多