【问题标题】:How to make JWT cookie authentication in Laravel如何在 Laravel 中进行 JWT cookie 身份验证
【发布时间】:2017-06-15 02:03:09
【问题描述】:

我想在 Laravel >=5.2 中使用 this (Tymon JWT-auth) library 进行 JWT 身份验证,但我想将 JWT 令牌放入 HttpOnly Cookies - 以保护 JWT 令牌免受 XSS 攻击。

  1. 我在项目中设置了 Tymon 库和...删除'middleware' => 'web'(如果我不这样做,我会看到发布请求的 CSRF 问题)。
  2. 在 routes.php 我写:
Route::group(['middleware' =>'api', 'prefix' => '/api/v1', 'namespace' => 'Api\V1'], function () {
    Route::post('/login', 'Auth\AuthController@postLogin');
    ...
    Route::get('/projects', 'ProjectsController@getProjects');
}
  1. 可能在 Api\V1\Auth\AuthController@postLogin 我生成令牌并将其作为 httpOnly cookie 发送回:

    ...
    try
    {
        $user = User::where('email','=',$credentials['email'])->first();
    
        if ( !($user && Hash::check($credentials['password'], $user->password) ))
        {
            return response()->json(['error' => 'invalid_credentials'], 401);
        }
    
        $customClaims = ['sub' => $user->id, 'role'=> $user->role, 'csrf-token' => str_random(32) ];
        $payload = JWTFactory::make($customClaims);
        $token = JWTAuth::encode($payload);
    } catch(...) {...}
    return response()->json($payload->toArray())->withCookie('token', $token, config('jwt.ttl'), "/", null, false, true); 
    
  2. 而且,是的,问题从这里开始。我想对每个请求做一些事情(可能是修改 laravel Auth 类):

    • 从请求中获取 cookie
    • 解码
    • 检查是正确的(如果不是 trhow 401)
    • 从数据库中获取用户
    • 并使该方法 Auth::user() 在 laravel 中以通常的方式在任何地方工作(例如,我可以在每个控制器中使用它)

任何想法如何做第 4 点?

更新

我还在这里添加了对 CSRF 攻击的保护 - csrf-token 在 JWT 中,它也在登录请求的响应体中返回(所以 JS 可以访问这个 csrf-token)(我只返回 JWT 的公共部分登录响应中的令牌,整个 JWT 仅在 cookie 中返回,因此它是 XSS 安全的) - 然后前端 JS 必须将 csrf-token 复制到每个请求的标头中。然后中间件 JWTAuthentiacate(在我下面的回答中)将 csrf-token 标头与 JWT 有效负载中的 csrf-token 字段进行比较 - 如果它们相似,则请求通过 csrf 测试。

【问题讨论】:

  • @Atlas-Pio 那是很久以前的事了,我不记得了 - 2 年来我较少使用 Laravel 而更多地关注前端(Angular) - 但我认为 - 所有信息都在这个问题中- 在我下面的回答中 - 但是如果你有一些具体的问题来设置它 - 创建新的堆栈溢出问题并在评论中给我链接 - 也许我可以提供帮助

标签: api authentication cookies laravel-5 jwt


【解决方案1】:

您可以通过创建中间件来简单地做到这一点。

在 handle() 方法中,只需从请求中获取 cookie,对其进行解码并使用此 Laravel 方法使用 id 登录用户:

Auth::loginUsingId($userIdFromToken);

【讨论】:

  • 哇 - 是的 - 这是一个绝妙的主意!我实现了它并完全按照我想要的方式工作:)
【解决方案2】:

我以这种方式实现了@ŁukaszKuczmaja 的想法并且它有效! :) 。所以我在app/Http/Middleware/JWTAuthenticate.php 创建文件:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;
use JWTAuth;
use Tymon\JWTAuth\Token;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Illuminate\Session\TokenMismatchException;

class JWTAuthenticate
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string|null  $guard
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    {
        try {
            if(!$request->headers->has('csrf-token')) throw new TokenMismatchException();
            $rawToken = $request->cookie('token');
            $token = new Token($rawToken);
            $payload = JWTAuth::decode($token);
            if($payload['csrf-token'] != $request->headers->get('csrf-token')) throw new TokenMismatchException();
            Auth::loginUsingId($payload['sub']);
        } catch(\Exception $e) {
            if( $e instanceof TokenExpiredException) {
                // TODO token refresh here
            }
            return response('Unauthorized.', 401);
        }

        return $next($request);
    }
}

app\Http\Kernel.php@$routeMiddelware我添加一行:

'jwt.auth'    => \App\Http\Middleware\JWTAuthenticate::class,

我的路由文件现在看起来像这样:

Route::group(['middleware' =>'api', 'prefix' => '/api/v1', 'namespace' => 'Api\V1'], function () {

    Route::post('/login', 'Auth\AuthController@postLogin');

    Route::group(['middleware' =>'jwt.auth'], function () {
        Route::post('/projects', 'ProjectsController@postProjects');
        Route::get('/projects', 'ProjectsController@getProjects');
        Route::put('/projects/{project}', 'ProjectsController@putProjects');
        Route::delete('/projects/{project}', 'ProjectsController@deleteProjects');
    });

});

例如在app/Http/Controllers/Api/V1/ProjectsController.php 我有:

public function getProjects() {
    $uid = Auth::user()->id;
    return Project::where('user_id','=',$uid)->get();
}

【讨论】:

  • 卡米尔,嗨!我在 httponly cookie 中使用 jwt-auth,但我坚持使用 csrf-token 和 api auth。我应该如何从服务器获取令牌以及应该将其存储在哪里?我应该通过对 API 的第一个请求获得它吗?将它存储在本地存储中是个好主意吗?如果我将它与 cookie 一起发送并将其存储在浏览器存储中,它将与每个请求一起发送回服务器,这是没有意义的
  • 查看我的问题更新 - 详细信息就在那里。或者,您也可以为 API 启用 crsf 令牌,例如here
【解决方案3】:

其实你可以把每一个需要认证的路由放到一个路由组中,然后像这样添加中间件:

Route::group(['middleware' => ['jwt.auth']], function () {
    Route::patch('/profile', 'UserController@update');
});

中间件已经完成了您想要的操作,因此无需编写额外的逻辑。不要使用额外的句柄方法。

在您的即 UserController 中,您可以然后即

$user = \Auth::user();

也就是说,取决于你需要什么......

// assign fields
$user->save();

return 'success'; // or whatever you need

不要重新发明轮子并保持干燥。

【讨论】:

  • 我猜你没有理解这个问题。这不是关于使用开箱即用的 jwt,而是关于使用 jwt 作为 httpOnly cookie,而不是使用 Bearer auth。没有任何自定义中间件,您不能将 jwt 用作 httpOnly cookie。
猜你喜欢
  • 2016-03-21
  • 1970-01-01
  • 2016-05-18
  • 2020-03-18
  • 2020-01-05
  • 2020-12-19
  • 2017-05-06
  • 2021-12-24
  • 2019-01-07
相关资源
最近更新 更多