Laravel笔记:配置jwt登陆

Posted by xtong on October 20, 2021

jwt安装

  • php版本:7.4
  • laravel版本:8.54
1
composer require tymon/jwt-auth

jwt配置

1、发布配置文件

1
2
# 这条命令会在 config 下增加一个 jwt.php 的配置文件
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

2、生成加密密钥

1
2
# 这条命令会在 .env 文件下生成一个加密密钥,如:JWT_SECRET=foobar
php artisan jwt:secret

3、更新你的模型

如果你使用默认的 User 表来生成 token,你需要在该模型下增加一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 原来的 User
<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    // ... code ...
}

## 增加的代码
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
    use HasApiTokens, HasFactory, Notifiable;

    // ... code ...

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

4、注册两个 Facade

这两个 Facade 并不是必须的,但是使用它们会给你的代码编写带来一点便利。

  • config/app.php
1
2
3
4
5
6
7
8
9
10
11
<?php

return [
    ...
    'aliases' => [
            ...
            // 添加以下两行
            'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth',
            'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory',
    ],
]

5、修改 auth.php

  • config/auth.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php

return [
    // 开发接口,设置默认 guard 为 api
    'defaults' => [
        'guard' => 'api',
        'passwords' => 'users',
    ],
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        // 这里增加 guard api 并设置验证使用 jwt
        'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],
    ],
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => \App\Models\User::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],
    ...
  ]

6、创建 token 控制器

1
php artisan make:controller Api/v1/AuthController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php

namespace App\Http\Controllers\Api\v1;

use App\Http\Controllers\Controller;
use Tymon\JWTAuth\Facades\JWTAuth;

/**
 * @group auth 登陆相关接口
 *
 * <aside class="warning">用户登陆</aside>
 */
class AuthController extends Controller
{
    /**
     * Create a new AuthController instance.
     * 要求附带 name 和 password(数据来源users表)
     *
     * @return void
     */
    public function __construct()
    {
        // 这里额外注意了:官方文档样例中只除外了『login』
        // 这样的结果是,token 只能在有效期以内进行刷新,过期无法刷新
        // 如果把 refresh 也放进去,token 即使过期但仍在刷新期以内也可刷新
        // 不过刷新一次作废
        $this->middleware('auth:api', ['except' => ['login', 'logout']]);
        // 另外关于上面的中间件,官方文档写的是『auth:api』
        // 但是我推荐用 『jwt.auth』,效果是一样的,但是有更加丰富的报错信息返回
    }

    /**
     * Get a JWT via given credentials.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function login()
    {
        $credentials = request(['email', 'password']);

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

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

    /**
     * Get the token array structure.
     *
     * @param  string $token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        return response()->json([
            'token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth('api')->factory()->getTTL() * 60
        ]);
    }

    ...
}

7、注册路由

  • routes/api.php
1
2
3
4
5
6
7
8
9
<?php

Route::group([
    'prefix' => 'auth'
], function ($router) {

    Route::post('login', [\App\Http\Controllers\Api\v1\AuthController::class, 'login']);
    ...
});

实现 jwt token 过期时间动态设置(记住我)

jwt token 的过期时间在 config/jwt.php 中可以配置,默认1小时。

而实际项目中,前端登陆时有【记住我】这个功能,勾选后需要过期时间设置为7天。最终找到解决办法如下:

  • App\Http\Controllers\Api\v1\AuthController::login
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * login 用户登录
 *
 * Get a JWT via given credentials.
 * @bodyParam name string required 用户名。
 * @bodyParam password string required 密码(不用加密)。
 * @bodyParam remember bool 记住我(自动登录)。
 *
 * @return \Illuminate\Http\JsonResponse
 */
public function login()
{
    $credentials = request(['name', 'password']);

    if (request(['remember'])) {
        JWTAuth::factory()->setTTL(60 * 24 * 7);
    }

    $token = JWTAuth::attempt($credentials);

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

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

软件版本:laravel 8.54

参考资料