为什么使用 jwt-auth 的会话时间是从登录时间算起 1 小时



在 laravel 7 后端 rest api 应用程序中,我使用 jwt-auth 并且登录时遇到问题 我可以在前端部分工作,但在 1 小时内我得到了TOKEN_EXPIRED错误。

1)我试图设置更大的会话时间,但失败了。在开发阶段,我需要更多的会议时间 1 小时。 继续直播,我将设置会话时间 30 分钟。

2)我预计会话更大是从登录用户的最后一次请求到后端的1小时,而不是从登录开始

我在下面实现了刷新方法,但看起来刷新不起作用...

app/Http/Controllers/API/AuthController.php :

<?php
namespace AppHttpControllersAPI;
use ApplibraryCheckValueType;
use AppSettings;
use Auth;
use Config;
use DB;
use Validator;
use CarbonCarbon;
use IlluminateHttpRequest;
use AppHttpControllersController;
use AppUser;
use IlluminateSupportStr;
use AppUserGroup;
use AppHttpResourcesUserCollection;
use Avatar;
use Storage;
class AuthController extends Controller
{
/**
* Create a new AuthController instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('jwt.auth', ['except' => ['login', 'register', 'activate']]);
}
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
if ($token = $this->guard('api')->attempt($credentials)) {
$loggedUser = $this->guard('api')->user();
if ($loggedUser->status != 'A') {
return response()->json(['error' => 'Unauthorized'], HTTP_RESPONSE_NOT_UNAUTHORIZED);
}
$loggedUser->last_logged = Carbon::now(config('app.timezone'));
$loggedUser->save();
$userGroupsCount = UserGroup
::getByUserId($loggedUser->id)
->count();
if ($userGroupsCount == 0) {
return response()->json(['error' => 'Unauthorized'], HTTP_RESPONSE_NOT_UNAUTHORIZED);
}
return $this->respondWithToken($token);
}
return response()->json(['error' => 'Unauthorized'], HTTP_RESPONSE_NOT_UNAUTHORIZED);
}

public function me()
{
return response()->json($this->guard('api')->user());
}
public function logout()
{
$this->guard('api')->logout();
return response()->json(['message' => 'Successfully logged out']);
}
public function refresh()
{
return $this->respondWithToken($this->guard()->refresh());
}
protected function respondWithToken($token)
{
$loggedUser = $this->guard()->user();
$user_avatar_path = 'public/' . User::getUserAvatarPath($loggedUser->id, $loggedUser->avatar);
$filenameData     = User::setUserAvatarProps($loggedUser->id, $loggedUser->avatar, true);
$usersGroups = User::getUserGroupByUserId($loggedUser->id, false);
return response()->json([
'access_token'     => $token,
'user'             => $loggedUser,
'token_type'       => 'bearer',
'user_avatar_path' => $user_avatar_path,
'filenameData'     => $filenameData,
'usersGroups'      => $usersGroups,
'expires_in'       => $this->guard('api')->factory()->getTTL() * 999360 // I SET VERY BIG VALUE
]);
}
public function guard()
{
return Auth::Guard('api');
}

在/config/auth.php 中

<?php
return [

'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
'hash' => false,
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => AppUser::class,
],
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 99360, // I SET BIG VALUE
'throttle' => 98860,
],
],

'password_timeout' => 10800,  // I SET BIG VALUE

在应用程序/异常/处理程序中.php我有:

public function render($request, Throwable $exception)
{
if ($exception instanceof UnauthorizedHttpException) {
if ($exception->getPrevious() instanceof TokenExpiredException) {
Log::info( '-2 UnauthorizedHttpException TokenExpiredException::' ); // I SEE THIS ERROR LOGGED
return response()->json(['error' => 'TOKEN_EXPIRED'], $exception->getStatusCode());

我有:

"laravel/framework": "^7.0",
"tymon/jwt-auth": "^1.0.0",

我的配置有什么问题?

改 性:

我添加了文件 app/Http/Middleware/JwtMiddleware.php包含内容和 1 行错误日志记录:

<?php
namespace AppHttpMiddleware;
use Closure;
use TymonJWTAuthExceptionsJWTException;
use SymfonyComponentHttpKernelExceptionUnauthorizedHttpException;
use TymonJWTAuthHttpMiddlewareBaseMiddleware;
use TymonJWTAuthExceptionsTokenExpiredException;
class JwtMiddleware extends BaseMiddleware
{
public function handle($request, Closure $next)
{
try {
if (! $user = $this->auth->parseToken()->authenticate()) {
return response()->json(['success' => false, 'error' => __('Invalid User.')]);
}
} catch (TokenExpiredException $e) {
try {
$refreshed = $this->auth->refresh($this->auth->getToken());
$user = $this->auth->setToken($refreshed)->toUser();
header('Authorization: Bearer ' . $refreshed);
} catch (JWTException $e) {
return response()->json(['success' => false, 'error' => __('Could not generate refresh token')]);
}
} catch (JWTException $e) {
Log::info( '-1 JwtMiddleware$e->getMessage() ::' . print_r(  $e->getMessage(), true  ) );
return response()->json(['success' => false, 'error' => __('Invalid request')]);
}
return  $next($request);
}
}

我在文件 app/Http/内核中添加.php :

...
protected $middleware = [
FruitcakeCorsHandleCors::class,
AppHttpMiddlewareTrustProxies::class,
AppHttpMiddlewareCheckForMaintenanceMode::class,
IlluminateFoundationHttpMiddlewareValidatePostSize::class,
AppHttpMiddlewareTrimStrings::class,
AppHttpMiddlewareJwtMiddleware::class,
IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull::class
];
...
and running the page site is broken and I see lines in log :
[2020-05-27 17:08:33] local.INFO: -1 JwtMiddleware$e->getMessage() ::The token could not be parsed from the request  
[2020-05-27 17:08:33] local.INFO: -1 JwtMiddleware$e->getMessage() ::The token could not be parsed from the request  
[2020-05-27 17:08:33] local.INFO: -1 JwtMiddleware$e->getMessage() ::The token could not be parsed from the request  

它是否与app/Http/Controllers/API/AuthController的代码有些冲突.php? 如何修复?

修改#2:谢谢!我修复了错误,并在 .env 中添加了行:

JWT_TTL=20 # 20 minutes
JWT_REFRESH_TTL=20160 # 2 weeks

并清除我再次记录的缓存,结果在我所做的应用程序中工作时 没有注销任何 20 分钟,但是当我离开应用程序时打开而不使用它 正如我所料,大约 30 分钟,我可以在没有 log ou 的情况下继续工作。 这些是其他选择吗?

修改#3:在我的vuejs文件的客户端部分,我最喜欢的是src/App.vue中的请求拦截器:

created() {
let self = this
this.$http.interceptors.response.use(undefined, function (error) {
return new Promise(function (/*resolve, reject*/) {
if (typeof error.response.status !== 'undefined' && error.response.status === 401) {
self.$store.dispatch('logout')  // DEBUGGING
self.showPopupMessage('Access', 'Not authorized !', 'warn')
let splitted0 = self.getSplitted(error.response.config.url, '/login', 0)
if (splitted0 == '') { // not move from login page
self.$router.push('/login')   // DEBUGGING
}
}
if (typeof error.response.status !== 'undefined') {
if (error.response.status === 401) {
self.$store.dispatch('logout') // DEBUGGING
self.showPopupMessage('Access', 'Not authorized !', 'warn')
self.$router.push('/login')  // DEBUGGING
}
}
throw error
})
})

}, //  created() {

我在其中捕获了401错误,并想知道是否可以捕获来自服务器的请求

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

并从$refreshed写入新的access_token值? 但是我怎样才能抓住它呢?但是一些特殊的退货请求代码?

谢谢!

转到---> config/jwt.php 你可以这样做

'ttl' => env('JWT_TTL', 1440)

控制器中的 OR

$token = JWTAuth::attempt($credentials, ['exp' => CarbonCarbon::now()->addDays(7)->timestamp]);

config()->set('jwt.ttl', 60*60*7);

Config::set('jwt.ttl', 60*60*7);

你应该有一个config/jwt.php文件。如果还没有此功能,请运行:

php artisan vendor:publish --provider="TymonJWTAuthProvidersLaravelServiceProvider"

在该文件中,您可以看到几个引用.env变量的属性,它们是:

  • 'ttl' (env = 'JWT_TTL')
  • "refresh_ttl"(环境 = "JWT_REFRESH_TTL")

此外,您可能希望将'refresh'添加到该jwt.auth中间件例外列表中,否则当您在控制器中调用refresh()函数时,它将不起作用。

但是,在此软件包中有一些重要事项需要注意:

身份验证 TTL 链接在一起

我相信您几乎可以忽略ttl属性,因为基于以下类比,实际定义 JWT 有效期的refresh_ttl

锁匠(身份验证提供者)给你一把红钥匙(JWT)。该钥匙允许您打开红门(使用您的后端)。按键在 5 分钟后自动折成两半 (TTL = 5 分钟)。但是,您可以带着损坏的钥匙回到锁匠那里,他将在 2 周内更换它(刷新 TTL = 2w)。

基本上,您可以使用过期的 JWT 调用auth/refresh终结点,它将刷新它,直到刷新 TTL 过期。

另请参阅[github上的此问题][1],这也解释了为什么您需要添加refresh函数以使其前面没有jwt.auth中间件。

使用此软件包提供的 JWT 时,您不会保存任何数据库调用

我最初的想法是,它可以节省我往返数据库的时间,因为用户对象将在 JWT 中签名并准备就绪。

我已经在几个项目中使用了这个包,我得出的结论是,它在某种程度上是一个美化的会话cookie。在tymon/jwt中,sub声明只是等于1(或这里引用的任何用户ID)。

这意味着您需要...

  • 。(默认)让tymon/jwt做的工作,它只是从数据库中检索用户并将其应用于您的身份验证提供程序,以便Auth::user()等都可以工作。

  • 。使用完整的用户对象自己添加一个声明,并在各个位置修改此包。这可以节省数据库调用,因为此对象将是 JWT 的一部分并在其中签名。它会导致在每个请求上发送更多的 JWT 字节,但您基本上可以将给定的对象视为理所当然(即在收到时接受它)并使用它。然后,您还可以以这种方式添加权限/角色,这样也不需要从数据库中检索这些权限/角色。此设置确实意味着在更改数据库中的任何权限时,您必须等待刷新新的 JWT 才能看到这些更改。

请在登录中尝试此操作

if ($token = $this->guard('api')->attempt($credentials,['exp' => CarbonCarbon::now()->addHours(2)->timestamp])) {
}

或者,您可以在config/jwt中更改ttl

让我知道它是否有帮助!

  • 您需要添加中间件并检查令牌是否已过期。
  • 如果已过期,请检查是否应生成新令牌的生成时间。 将此视为刷新令牌过期时间。 如果要在没有任何时间验证的情况下生成刷新令牌,可以跳过此步骤。
  • 现在,如果一切正常,请生成一个新令牌,并根据需要将其发送到标头/正文中,并将其存储在前端应用程序中。
<?php
namespace AppHttpMiddleware;
use Closure;
use TymonJWTAuthExceptionsJWTException;
use SymfonyComponentHttpKernelExceptionUnauthorizedHttpException;
use TymonJWTAuthHttpMiddlewareBaseMiddleware;
use TymonJWTAuthExceptionsTokenExpiredException;
class JwtMiddleware extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* @param  IlluminateHttpRequest  $request
* @param  Closure  $next
* @return mixed
*/
public function handle($request, Closure $next)
{
try {
if (! $user = $this->auth->parseToken()->authenticate()) {
return response()->json(['success' => false, 'error' => __('Invalid User.')]);
}
} catch (TokenExpiredException $e) {
try {
$refreshed = $this->auth->refresh($this->auth->getToken());
$user = $this->auth->setToken($refreshed)->toUser();
header('Authorization: Bearer ' . $refreshed);
} catch (JWTException $e) {
return response()->json(['success' => false, 'error' => __('Could not generate refresh token')]);
}
} catch (JWTException $e) {
return response()->json(['success' => false, 'error' => __('Invalid request')]);
}
return  $next($request);
}
}

更新:

这是我在最近的Ionic/Angular APP中遵循的方法。而且它工作正常。我以前做过很多方法,但很难解释,当你有任何问题时,请告诉我。

  • 当您成功登录并将两个令牌发送到您的前端应用程序并将其保存在本地存储中时,您会生成一个令牌和一个refresh_token。
  • 对于所有受保护的 API,您现在必须发送令牌和refresh_token。
  • 在 API 端验证令牌,如果已过期,请检查refresh_token是否有效或已过期。如果已过期,您将注销。
  • 如果refresh_token有效,则生成一个新令牌并refresh_token并将其发送到前端并存储在那里。
  • 这种方法有一个问题,只要用户正在使用APP,登录会话就应该处于活动状态。因此,假设您的JWT_REFRESH_TTL是 1 天,并且用户在 24 小时之前使用,它将在 24 小时通过之前工作,但它不会在 1 分钟后工作,因为令牌和refresh_token都将过期。
  • 为了克服这个问题,您可以使用每个 API 请求生成一个新refresh_token并在前端进行更新。

通常,JWT_TTL应该很短,例如 5 分钟左右,JWT_REFRESH_TTL应该是会话活动时间。

正如其他人向您建议的那样,转到您的config/jwt.php并修改令牌过期限制:

// ...
'ttl' => env('JWT_TTL', 3600)

重要提示:更改后,请清除配置缓存

php artisan config:clear

另请注意,以前生成的令牌将有自己的声明,因此这些令牌可能不会遵循修改后的规则。

旁注

作为自己实现JWT的替代方案,您可以使用Sanctum,一个新的第一方Laravel软件包。检查Sanctum文档中的SPA模式。

最新更新