Laravel JWT 的简单使用与浅度刨析(使用自定义Model)
一、安装与配置
安装:
使用最新的就行,使用时问题不在于版本,而在于你会不会读源码。
$ composer require tymon/jwt-auth
生成配置文件:
会在config
文件夹中生成一个jwt.php
文件。jwt.php
可以配置一些加密算法、token过期时间等信息。
$php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
生成加密密钥:
token
是由 header(base64算法加密) + payload(base64算法加密) + secret
再用HS256等算法加密后组成的。secret
是服务器保存的,下面命令会在.env
文件中生成一个随机的secret
。secret
要注意保管,不能泄露。.env
文件在.gitignore
文件中默认是被标记的,即.env
文件是不建议上传到 github 上,同样也就不建议将secret
上传到 github 上防止泄露。
$ php artisan jwt:secret
生成模型文件并执行迁移:
生成模型文件:
$ php artisan make:model DemoModel -m
-m
表示同时生成模型对应的迁移文件。(数据库迁移是个重要知识点,不懂的自行学习修改迁移文件:
因为是学习/测试,因此我把程序最简化,只需要name
和password
即可。执行迁移:
$ php artisan migrate
修改模型文件:
修改内容:
必须
extends User
这里as Authenticatable
了。
该User
类继承了许多接口,比如Authenticatable
接口,JWT
有部分功能使用到了 laravel 自带的Auth
功能,比如JWTAuth::attempt()
函数,这个函数是JWTAuth
类调用的,而并不是Auth
调用,但它的调用却需要Authenticatable
类实例,因此如果不extends User
的话,会抛出validateCredentials() must be an instance of Illuminate\Contracts\Auth\Authenticatable
异常。
这也是为什么可以直接使用auth("api")->attempt(xxx)
进行验证的原因。
如下:JWTAuth::attempt()
函数中,byCredentials()
函数需要Authenticatable
类实例。因为是学习/测试,因此我把程序最简化,只需要
name
和password
即可。添加
implements JWTSubject
接口,并继承以下方法:public function getJWTIdentifier() { return $this->getKey(); } public function getJWTCustomClaims() { return []; }
我理解的含义是,将
JWT
与Model
进行映射,因为我们需要使用JWT
生成token
,而生成时需要传入JWTSubject
实例对象,但实际我们传入的是App\Models\DemoModel
实例对象,比如JWTAuth::fromSubject(instance of JWTSubject)
。
修改 Config 配置文件:
- 修改
app.php
配置文件:
给aliases
添加以下两个字段:
添加该别名的好处是,能直接在头部'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class, 'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
use JWTAuth;
便可使用JWTAuth::XXX
,而无需use Tymon\JWTAuth\Facades\JWTAuth;
长长的 path 。但你use JWTAuth;
时会发现使用JWTAuth::XXX
vscode 编辑器会报错,这是因为编辑器并没有识别别名的特性,在报错下直接发请求,也是可以正常响应的。
如果想直接use 长长的 path
,可以不用添加这两行别名,因为有些编辑器会自动引入use XXX
。 - 修改
auth.php
配置文件:
- 使用的守卫是
api
(因为我们前后端分离,路由是从/api
进入的); - 将
api
的driver
改为jwt
; - 既然我们要使用
DemoModel
的eloquent
,那么就要model=>App\Models\DemoModel::class
。
二、创建路由与控制器(简单起见这里不使用中间件)
- 创建路由:在
routes/api.php
下
so easy! - 创建控制器:
$ php artisan controller:DemoController
三、控制器(Controller)
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\DemoModel;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Facades\JWTAuth;
class DemoController extends Controller
{
public function register(Request $request)
{
$credentials = [
'name' => $request->name,
'password' => $request->password
];
$dm = DemoModel::create($credentials);
if ($dm) {
// 生成 token
$token = JWTAuth::fromSubject($dm);
return var_dump($token);
}
}
public function login(Request $request)
{
// 验证 token
return var_dump((JWTAuth::parseToken()->check()));
}
}
var_dump()
和dd()
用法差不多,var_dump()
能输出更详细的内容。
四、测试
- 测试工具:postman
- 用户注册——服务端会生成
token
响应给客户端(postman):
发送 Post 请求,注意 url 中必须加上/api/
,这是因为我们使用的是routes/api.php
。不清楚
api.php
和web.php
的区别可以百度。
复制下面的
token
,不用复制引号。
- 用户登陆——客户端(postman)会携带
token
给服务器验证:
把刚复制的token
粘贴到这里:
会响应true
,可以点击Preview
看的更清晰:
五、对 JWTAuth 类成员的简单刨析
fromSubject(JWTSubject $subject)
:
- 生成一个
token
。需传入的JWTSubject
对象就是一个Model
对象。
fromUser(JWTSubject $user)
:
fromSubject
的别名,调用fromUser
等于调用fromSubject
:
$token
:
- 当前
JWT
实例中保存的token
对象,非string
类型。
getToken()
:
- 从当前
JWT
实例中获取token
对象。 $token
为null
时会调用parseToken()
函数解析Request
中的token
,之后再调用setToken()
将解析后的token
转换为token
对象,最后将其存入$this->token
中。
parseToken()
:
- 由上面的代码可知,第一步
$this->parseToken()
的返回值仍然是$this
。也就是说,parseToken()
函数的作用仅仅是给$this->token
赋予一个有意义的token
对象,当然如果赋予失败,则$this->token = null
。
setToken($token)
:
- 检查传入的
Token对象|string
是否满足【段数要求、令牌格式要求】,并将string
类型转换成token
对象。setToken()
的返回值是$this
,而并非token
对象。 parseToken()
和setToken()
的区别在于parseToken()
会先从Request
中解析出token
。token
对象成员很简单:$value
为string
类型的token
,get()
的作用就是返回value
。
check($getPayload = false)
函数详解:
该函数上面的注释说Check that the token is valid.
,但直接使用JWTAuth::check()
时却会发现始终返回false
,以为这是token
本身的问题(过期了呀什么的)。我排查了很久才发现,这是因为发生异常时返回的是false
!而并没有被进行抛出,这可真是令人尴尬…
我把源码的false
改成$e
,并使用JWTAuth::check()
来验证token
:
注意这里不用var_dump()
或dd()
函数,直接return JWTAuth::check()
,这样看错误会更清楚一些。
会发现错误是在执行Tymon\JWTAuth\JWT->requireToken()
这个函数时抛出的。
找到它:
这不就是上面介绍的$this->token
吗…
原来在进行JWTAuth::check()
前,我们需要对$this->token
进行赋值。我在上面介绍过,parseToken()
函数能给$this->token
赋值,那就来尝试一下:
先把改过的源码改回来的:
$e
改回false
。
postman
返回了true
!
这里使用了
var_dump
,不用会返回1,编程中true即1,false即0
我们再来测试一下错误的token
:
先把要请求的token
值随意增删改,
点击Send
:
返回了false
!
要调用验证方法就得需要写两行代码,这明显变得复杂了:
因此我们通常使用:JWTAuth::parseToken()->check();
一行代码来验证请求发来的token
。
本作品采用《CC 协议》,转载必须注明作者和本文链接