登录
原创

laravel Sanctum 认证 — API令牌认证

发布于 2020-10-15 阅读 10556
  • 后端
  • PHP
  • API
  • Laravel
原创

laravel Sanctum介绍

Laravel Sanctum 为 SPA (单页面应用程序)、移动应用程序和简单的、基于令牌的 API 提供轻量级身份验证系统。Sanctum 允许应用程序的每个用户为他们的帐户生成多个 API 令牌。这些令牌可能被授予指定允许令牌执行哪些操作的能力 / 范围。

工作原理

Laravel Sanctum 是为了解决两个独立问题而生。

API 令牌

它是一个简单的包,用于向用户发出 API 令牌,同时可以通过Sanctum来生成和管理这些令牌。

例如:

  1. 当用户登信息校验通过,可以发放令牌用于后续信息核验;
  2. 类似于 GitHub 的「访问令牌」,用户通过控制台页面生成一个令牌提供给其他用户使用。

Laravel Sanctum 的这个特性是通过将用户 API令牌存储在单个数据库表中,并通过包含了有效 API 令牌的 Authorization标头对传入请求进行身份验证而实现的。

这些令牌通常有很长的过期时间 (以年计),当然用户是可以随时手动撤销它们的。

SPA 身份验证

Sanctum 提供了一种简单的方法来认证需要与基于 Laravel 的 API 进行通信的单页应用程序 (SPAs)。这些 SPA 可能与 Laravel 应用程序存在于同一仓库中,也可能是一个完全独立的仓库,例如使用 Vue CLI 创建的单页应用程序。

对于此功能,Sanctum 不使用任何类型的令牌。相反,Sanctum 使用 Laravel 内置的基于 cookie 的会话身份验证服务。这提供了 CSRF 保护,会话身份验证以及防止因 XSS 攻击而泄漏身份验证凭据。仅当传入请求来自您自己的 SPA 前端时,Sanctum 才会尝试使用 Cookie 进行身份验证。

安装

1、 你可以通过 Composer 安装 Laravel Sanctum:

composer require laravel/sanctum

2、你需要使用 vendor:publish Artisan 命令发布 Sanctum 的配置和迁移文件。Sanctum 的配置文件将会保存在 config 文件夹中:

php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

3、你需要执行数据库迁移文件。Sanctum 将创建一个数据库表用于存储 API 令牌:

php artisan migrate

注:由于这里是讲解API令牌认证,所以下面不需要操作

4、假如你需要使用 Sanctum 来验证 SPA,你需要在 app/Http/Kernel.php 文件中将 Sanctum 的中间件添加到你的 api 中间件组中:

use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;

'api' => [
    EnsureFrontendRequestsAreStateful::class,
    'throttle:60,1',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

自定义迁移

如果你不想使用 Sanctum 的默认迁移,你可以在你的 AppServiceProvider 中的 register 里调用 Sanctum::ignoreMigrations 方法。 你可以使用 php artisan vendor:publish --tag=sanctum-migrations 导出默认迁移。

API令牌认证

令牌发放场景

1、用户主动触发发放:例如登录、api获取等等

2、管理员发放:例如对特定用户开发资源访问的时候,通过sanctum进行临牌发放

当使用 API 令牌进行请求的时,令牌可以以 Bearer 的形式包含在 Authorization header 头里。

令牌发放

给用户发行令牌的时候,User 模型里应该使用 HasApiTokens trait:

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
}

发行一个令牌,需要使用 createToken 方法。createToken 方法返回一个 Laravel\Sanctum\NewAccessToken实例。
在存入数据库之前,API 令牌已使用 SHA-256 哈希加密过,但是可以用 NewAccessToken 实例的 plainTextToken 属性访问令牌的纯文本值。

令牌创建后,应该立即向用户展示这个纯文本值:

//创建API令牌
$token = $user->createToken('token-name');

//获取令牌明文
return $token->plainTextToken;

//令牌:6|F9cyAXAb7yxWpOUDq7nGfzFIt7K7Ag0bfuN1xC8O4IjnaWQNt7djMVacyphjqmVKmCC6dSIMHDSuLGQd

可以使用 HasApiTokens trait 提供的 tokens Eloquent 关联关系来获取所有的用户令牌:

foreach ($user->tokens as $token) {
    //
}

令牌能力(权限)

Sanctum 可以为令牌分配 “abilities”,类似于 OAuth 的“scopes”。可以将字符串能力数组作为第二个参数传递给 createToken 方法:

//1、可以限制令牌拥有哪些权限
return $user->createToken('token-name', ['server:view'])->plainTextToken;

//2、在使用 Sanctum 处理一个请求的时候,可以使用 tokenCan方法来决定令牌是否具有给定的能力:
if ($user->tokenCan('server:update')) {
    //
}

路由令牌校验

为了保护路由,所有进来的请求都必须进行认证,应该将 Sanctum 认证守卫附加到routes/api.php 的 API 路由里:

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

令牌销毁

我们可以使用 HasApiTokens trait 提供的 tokens关联关系从数据库删除它们,以达到撤销令牌的目的:

// 撤销所有令牌...
$user->tokens()->delete();

// 撤销用户最近的令牌...
$request->user()->currentAccessToken()->delete();

// 撤销一个特定的令牌...
$user->tokens()->where('id', $id)->delete();

API令牌扩展

场景

由于API令牌明文组成是ID|Token格式,既不利于记忆也暴露了token主键所以,最好是将明文进行再次加密:
//令牌明文:6|F9cyAXAb7yxWpOUDq7nGfzFIt7K7Ag0bfuN1xC8O4IjnaWQNt7djMVacyphjqmVKmCC6dSIMHDSuLGQd
//令牌密文:AXAb7yxWpOUDq7nGfzFIt7K7Ag0bfuN1xC8O4IjnaWQNt7djMVacyphjqmVKmCC6dSIMH

token加密扩展

1、创建加解密方法


    /**
     * 加密
     * @param $data
     * @param $key 16位字符
     * @param $iv  16位字符
     * @return string
     */
    function encryptHash($token,$key,$iv)
    {
        $encrypt = openssl_encrypt($token, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
        return base64_encode($encrypt);
    }

    /**
     * 解密
     *
     * @param $data
     * @param $key
     * @param $iv
     * @return string
     */
    function decryptHash($token,$key,$iv)
    {
        $token = base64_decode($token);
        return openssl_decrypt($token, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
    }

2、加密明文令牌

//创建API令牌
$token = $user->createToken('token-name');

//获取令牌明文
return encryptHash($token->plainTextToken,$key,$iv);

3、重写PersonalAccessToken token校验

创建PersonalAccessToken文件同时继承Laravel\Sanctum\PersonalAccessToken,重写令牌查询方法,在方法中对令牌进行解密操作,再进行处理匹配查询


use Laravel\Sanctum\PersonalAccessToken as BaseToken;

class PersonalAccessToken extends BaseToken
{
    /**
     * Find the token instance matching the given token.
     *
     * @param string $token
     * @return static
     */
    public static function findToken($token)
    {
        if ($_token = decryptHash($token,$key,$iv)) {
            $token = $_token;
        }

        if (strpos($token, '|') === false) {
            return static::where('token', hash('sha256', $token))->first();
        }

        [$id, $token] = explode('|', $token, 2);

        if ($instance = static::find($id)) {
            return hash_equals($instance->token, hash('sha256', $token)) ? $instance : null;
        }
    }
}

4、重新指定Sanctum的accessToken处理model

 //重新指定Sanctum的accessToken处理model
Sanctum::$personalAccessTokenModel = 'App\\Models\\SdkCn\\Common\\PersonalAccessToken';

到此完成了laravel Sanctum认证的API令牌认证功能,以及对token明文加密解密的扩展功能

评论区

励志做一条安静的咸鱼,从此走上人生巅峰。

0

0

0

举报