如果不用于令牌,Sanctum 和 Laravel 的默认身份验证是否相同?

Pou*_*nte 12 laravel eloquent laravel-5

我不太确定 Laravel 文档中的含义,所以我要求确定。

我们一方面有 Laravel 的默认身份验证,另一方面有 Sanctum 的默认身份验证。

据说 Sanctum 既可以做代币,也可以简单地实现身份验证。:

对于此功能,Sanctum 不使用任何类型的令牌。相反,Sanctum 使用 Laravel 内置的基于 cookie 的会话身份验证服务。这提供了 CSRF 保护、会话身份验证以及防止身份验证凭据通过 XSS 泄漏的好处。当传入请求来自您自己的 SPA 前端 (Vue.js) 时,Sanctum 只会尝试使用 cookie 进行身份验证。

因此,如果从不使用令牌,Sanctum 与默认的身份验证方法基本相同,对吗?基本上,它是否实现了默认身份验证并在需要时添加令牌?如果是这样,sanctum 和护照有什么区别,因为它们做同样的事情,但据说 Sanctum 是轻量级的。这实际上意味着什么?

谢谢阅读

Aru*_*A S 12

因此,如果从不使用 Tokens,Sanctum 与默认的 Authentication 方法基本相同,对吗?

是的,在幕后它使用 laravel 的默认身份验证。

看一下 sanctum 守卫(下面的代码取自 github。最后一次提交是在 4 月 11 日,sanctum 2.x)

<?php

namespace Laravel\Sanctum;

use Illuminate\Contracts\Auth\Factory as AuthFactory;
use Illuminate\Http\Request;

class Guard
{
    /**
     * The authentication factory implementation.
     *
     * @var \Illuminate\Contracts\Auth\Factory
     */
    protected $auth;

    /**
     * The number of minutes tokens should be allowed to remain valid.
     *
     * @var int
     */
    protected $expiration;

    /**
     * Create a new guard instance.
     *
     * @param  \Illuminate\Contracts\Auth\Factory  $auth
     * @param  int  $expiration
     * @return void
     */
    public function __construct(AuthFactory $auth, $expiration = null)
    {
        $this->auth = $auth;
        $this->expiration = $expiration;
    }

    /**
     * Retrieve the authenticated user for the incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */
    public function __invoke(Request $request)
    {
        if ($user = $this->auth->guard(config('sanctum.guard', 'web'))->user()) {
            return $this->supportsTokens($user)
                        ? $user->withAccessToken(new TransientToken)
                        : $user;
        }

        if ($token = $request->bearerToken()) {
            $model = Sanctum::$personalAccessTokenModel;

            $accessToken = $model::findToken($token);

            if (! $accessToken ||
                ($this->expiration &&
                 $accessToken->created_at->lte(now()->subMinutes($this->expiration)))) {
                return;
            }

            return $this->supportsTokens($accessToken->tokenable) ? $accessToken->tokenable->withAccessToken(
                tap($accessToken->forceFill(['last_used_at' => now()]))->save()
            ) : null;
        }
    }

    /**
     * Determine if the tokenable model supports API tokens.
     *
     * @param  mixed  $tokenable
     * @return bool
     */
    protected function supportsTokens($tokenable = null)
    {
        return $tokenable && in_array(HasApiTokens::class, class_uses_recursive(
            get_class($tokenable)
        ));
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你检查_invoke()方法,

    if ($user = $this->auth->guard(config('sanctum.guard', 'web'))->user()) {
        return $this->supportsTokens($user)
                    ? $user->withAccessToken(new TransientToken)
                    : $user;
    }
Run Code Online (Sandbox Code Playgroud)

使用身份验证的用户找到

$user = $this->auth->guard(config('sanctum.guard', 'web'))->user()
Run Code Online (Sandbox Code Playgroud)

在检查了 sanctum 配置文件后,目前没有sanctum.guard配置(它可能意味着未来的某个版本),所以web默认情况下sanctum 会检查守卫,所以它基本上和你的默认 web 路由做同样的事情。

但是你误解了 Sanctum 的用法。Sanctum 用于 API 身份验证而不是 Web 身份验证(尽管它也可以用于 Web 身份验证)。Sanctum 的非令牌身份验证使您的 SPA 能够使用与移动应用程序(使用令牌身份验证)相同的 API,而无需令牌并提供 csrf 和基于会话的身份验证的好处。

为了帮助您更好地理解,假设您已经构建了一个使用令牌进行身份验证的 API(如果它已经使用 sanctum 作为令牌,这会使事情变得更简单)。现在您希望构建一个使用相同 API 的 SPA(可以在 laravel 项目本身内部构建,也可以在同一个域或不同域上构建一个单独的项目),但是由于这将由您构建,因此它是一个受信任的站点,因此您不希望它使用令牌,而是使用 Laravel 基于默认会话的身份验证以及 csrf 保护,同时还使用相同的 api 路由。SPA 将通过 ajax 与服务器通信。您还希望确保只允许您的 SPA 使用基于会话的身份验证,而不允许其他第三方站点使用它。

所以这就是 Sanctum 的用武之地。您只需要将 Sanctum 中间件添加到您的api路由组中app/Http/Kernel.php

use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;

'api' => [
    EnsureFrontendRequestsAreStateful::class,
    'throttle:60,1',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],
Run Code Online (Sandbox Code Playgroud)

然后配置 sanctum 以允许您的 SPA 域并配置 cors(查看文档以了解如何执行此操作)。然后只需将auth:sanctum中间件添加到您的路由中,您就完成了服务器端设置。

现在,如果请求具有令牌或者它是有状态的(会话 cookie),这些路由将对用户进行身份验证。

现在您的 SPA 可以在没有令牌的情况下与您的 API 进行通信。

要获得 csrf 保护,请先调用csrf-cookie请求,这将在您的 cookie 中设置 csrf 令牌,axios 会自动将其附加到后续请求中

axios.get('/sanctum/csrf-cookie').then(response => {
    // Login...
})
Run Code Online (Sandbox Code Playgroud)

sanctum 和护照有什么区别,因为它们做同样的事情,但据说 Sanctum 是轻量级的。

嗯,正如它所说的,sanctum 是轻量级的。这是因为 Passport 提供了完整的 Oauth 功能,而 Sanctum 只专注于创建和管理令牌。为了用简单的方式解释 Oauth,您必须在不同的站点上看到那些Sign in with Google, Sign in with Facebook,Sign in with Github然后您可以使用您的 google/facebook/github 帐户将其签名到这些站点。这是可能的,因为谷歌、Facebook 和 Github 都提供了 Oauth 功能(只是一个简单的例子,不详述)。对于大多数网站,您实际上并不需要 Passport,因为它提供了许多您不需要的功能。对于简单的 api 身份验证 Sanctum 绰绰有余


agm*_*984 6

注意:此答案适用于 Laravel Sanctum + 同域 SPA

要添加到这些答案中,默认的 Laravel 身份验证使用web保护,因此您必须将其用于身份验证路由(对于同域 SPA 应用程序)。

例如,您可以创建自己的路由,指向 Laravel 的RegistersUserstrait 和AuthenticatesUserstrait:

网页.php

Route::group(['middleware' => ['guest', 'throttle:10,5']], function () {
    Route::post('register', 'Auth\RegisterController@register')->name('register');
    Route::post('login', 'Auth\LoginController@login')->name('login');

    Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');
    Route::post('password/reset', 'Auth\ResetPasswordController@reset');

    Route::post('email/verify/{user}', 'Auth\VerificationController@verify')->name('verification.verify');
    Route::post('email/resend', 'Auth\VerificationController@resend');

    Route::post('oauth/{driver}', 'Auth\OAuthController@redirectToProvider')->name('oauth.redirect');
    Route::get('oauth/{driver}/callback', 'Auth\OAuthController@handleProviderCallback')->name('oauth.callback');
});

Route::post('logout', 'Auth\LoginController@logout')->name('logout');
Run Code Online (Sandbox Code Playgroud)

但请确保它们在web.php文件中。例如,如果它们在api.php文件中,我会看到一些关于session store not on request并且RequestGuard::logout()不是函数的奇怪错误。我相信这与$this->guard()auth 特征中的默认 auth guard via有关,并且与api.php/api前缀有关。

/api,因为如果你使用的作曲家包齐格实现前缀似乎相关route('login')route('logout'),他们实际上决心/api/login/api/logout

我怀疑这导致了 Sanctum 的问题。解决方法是确保路由在web.php. 如果他们的配置相似,或者例如,如果他们Auth::routes()api.php.

仔细检查您的Kernel.php(它应该是这样的):

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        // \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    'api' => [
        EnsureFrontendRequestsAreStateful::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'throttle:60,1',
    ],
];
Run Code Online (Sandbox Code Playgroud)

如果您StartSessionapi中间件组中,则您的配置不正确或不必要地复杂。

这是我的工作./config/auth.php文件,供您比较:

'defaults' => [
    'guard' => 'web',
    'passwords' => 'users',
],

...

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'token',
        'provider' => 'users',
        'hash' => false,
    ],
],
Run Code Online (Sandbox Code Playgroud)

然后,您可以将guest中间件用于登录/注册路由,非常重要的是,您应该在其中声明所有 JSON 服务端点api.phpauth:sanctum在这些路由上使用中间件。

一旦您认为它可以正常工作,我会为您提供两个测试/调试步骤:

  • 打开 Chrome > 开发工具面板
  • 转到应用程序选项卡
  • 检查以确保有两个 cookie:<app_name>_session, 和XSRF-TOKEN
  • 使用记住我复选框和remember: true登录有效负载,确保有第三个 cookieremember_web_<hash>
  • 确保会话 cookie 是httpOnly,并确保 CSRF cookie 不是(以便您的 JavaScript 可以访问它)

、在您的单元测试中,确保在 之后$this->postJson(route('login'), $credentials),您会看到:

  • Auth::check() 应该返回真
  • Auth::user() 应该返回用户对象
  • Auth::logout()应该将用户注销,然后立即$this->assertGuest('web');返回 true

在您验证这两个步骤之前不要太兴奋,一旦您成功验证了这些步骤,请不要太兴奋。这意味着您正在使用 Laravel 的默认身份验证逻辑。

为了更好地衡量,以下是通过 JavaScript 附加 CSRF 令牌的示例:

import Cookies from 'js-cookie';

axios.interceptors.request.use((request) => {
    try {
        const csrf = Cookies.get('XSRF-TOKEN');

        request.withCredentials = true;

        if (csrf) {
            request.headers.common['XSRF-TOKEN'] = csrf;
        }

        return request;
    } catch (err) {
        throw new Error(`axios# Problem with request during pre-flight phase: ${err}.`);
    }
});
Run Code Online (Sandbox Code Playgroud)