JWT-Json Web Token,一种基于json格式的开放标准,常常被用作替代cookie的认证方式,特别适合前后端分离的WEB应用,以及api接口。今天就讲讲如何在Laravel应用中使用JWT,虽然网上找到的Laravel集成JWT的方法,不过要么就坑点太多,要么就有诸多限制(比如要验证的模型有多个怎么配置)。
实验环境
Laravel 5.2+
PHP 5.5+
tymon/jwt-auth 1.0.0-beta.3 (十分重要)
起步 利用composer安装jwt-atuh 在你的Laravel项目目录里composer.json补充上以下
1
2
3
4
5
{
"required" : {
"tymon/jwt-auth" : "1.0.0-beta.3" # 这个版本很急很关键,我用这个版本才能跑通..Orz
}
}
然后安装一下依赖
配置Laravel的服务提供者及其门面 在/config/app.php里找到对应的数组,补充以下东西
1
2
3
4
5
6
7
'providers' => [
Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
]
'aliases' => [
'JWTAuth' => Tymon\JWTAuth\Facades\JWTFactory::class,
]
配置你的用户表(用户模型) 因为Jwt-auth默认用的是Laravel的auth权限管理,Laravel 的权限管理默认用的是项目一开始就有的User模型(/app/User.php),下面姑且先用着Laravel的User模型为例,至于如何自定义用户模型使用其他表,我待会再讲
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
<?php
namespace App ;
use Illuminate \Foundation \Auth \User as Authenticatable ;
use Tymon \JWTAuth \Contracts \JWTSubject ;
class User extends Authenticatable implements JWTSubject
{
protected $fillable = [
'name' , 'email' , 'password' ,
];
protected $hidden = [
'password' , 'remember_token' ,
];
public function getJWTIdentifier ()
{
return $this ->getKey();
}
public function getJWTCustomClaims ()
{
return [];
}
}
可见,配置用户表只需继承Illuminate\Foundation\Auth\User这个类,实现JWTSubject这个接口的两个方法即可。不过这个表必须要有password这个字段,必须有主键, 至于如果你的用户表不用password而是用userPass等等自己自定义的字段名怎么办,这个我待会再讲~
配置Laravel的auth /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
'defaults' => [
'guard' => 'api' ,
'passwords' => 'users' ,
],
'guards' => [
'web' => [
'driver' => 'session' ,
'provider' => 'users' ,
],
'api' => [
'driver' => 'jwt' ,
'provider' => 'users' ,
],
],
'providers' => [
'users' => [
'driver' => 'eloquent' ,
'model' => App\User::class,
],
],
生成JWT密钥 1
2
$ php artisan vendor:publish # 将jwt的配置文件自动拉到/config目录
$ php artisan jwt:secret # 自动生成JWT密钥,具体可看.env文件
实际业务逻辑 可以直接在你的控制器里进行调试
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
<?php
namespace App \Http \Controllers ;
use App \User ;
use Illuminate \Http \Request ;
use Illuminate \Support \Facades \Auth ;
use Tymon \JWTAuth \Facades \JWTAuth ;
class TestController extends Controller
{
public function register ()
{
User::create([
'name' => 'Dawnki' ,
'phone' => '188xxxxxx' ,
'password' => bcrypt('123456' )
]);
}
public function login (Request $request)
{
$token = JWTAuth::attempt(['phone' =>'188xxxxxx' ,'password' =>123456 ]);
return response(json_encode(['token' =>$token]),200 );
}
public function index (Request $request)
{
return Auth::guard('api' )->user();
}
}
给index方法(即实际业务里的内部需要登陆后才能访问的接口)编写一个中间件 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
<?php
namespace App \Http \Middleware ;
use Closure ;
use Illuminate \Support \Facades \Auth ;
class UserMiddleware
{
protected $auth;
function __construct (Auth $auth)
{
$this ->auth = $auth;
}
public function handle ($request, Closure $next, $guard = 'api' )
{
if (!($user = Auth::guard($guard)->user())) {
return response('token' ,403 );
}
return $next($request);
}
}
此处只显示中间件的关键方法,至于如何给内部接口(如上例中的index方法)配置中间件自行解决,关键的验证即是 Auth::guard(‘api’)->user(); 当前端把Token值放入Header中, 即 “Authorization” : “Bearer token值”(注意Bearer与token值中的空格 ). Jwt-Auth结合了Laravel的Auth就会通过user()方法自动从Header中取出token值 ,并且从中解密找出隐含在其中的用户摘要(id).
注意坑点:用户表的加密方式 务必使用bcrypt(即哈希加密) ,不要使用Crypt门面提供的加密,不要使用Crypt门面提供的加密,不要使用Crypt门面提供的加密!Crypt的加密不适合数据库迁移!
自定义你的用户模型 首先自定义用户模型,不使用自带的User模型 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
<?php
namespace App ;
use Illuminate \Database \Eloquent \Model ;
use Illuminate \Foundation \Auth \User ;
use Tymon \JWTAuth \Contracts \JWTSubject ;
class Admin extends User implements JWTSubject
{
protected $table="admin" ;
protected $primaryKey="admin_id" ;
protected $fillable=["admin_name" ,"adminPass" ];
protected $guarded='' ;
protected $hidden=["adminPass" ];
public function getJWTIdentifier ()
{
return $this ->getKey();
}
public function getJWTCustomClaims ()
{
return [];
}
public function getAuthPassword ()
{
return $this ->getAttribute('adminPass' );
}
}
如上,只需继承User,实现JWTSubject。另外如果你表密码字段不是password,而是其他(如上的adminPass),只需重写getAuthPassword方法(来自于Illuminate\Foundation\Auth\User中用到的trait)即可
然后在/config/auth.php中修改
1
2
3
4
5
6
<?php
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Admin::class, //你需要的类
]
然后你就可以使用自己编写的用户模型,又能搭配上JWT了!
另外此处有一个坑点就是你自定义的密码字段, 在attempt验证账号登陆时是要注意一些细节
1
2
3
4
5
6
7
8
9
10
<?php
public function login (Request $request)
{
$token = JWTAuth::attempt(['phone' =>'188xxxxxx' ,'password' =>123456 ]);
return response(json_encode(['token' =>$token]),200 );
}
上例,我自定义的密码字段为adminPass,在调用attempt时,不要顺理成章的在验证数组里使用adminPass,依然是使用’password’ ,这是由于attempt方法内部的限定,在下一篇博客将详细介绍为何要这样~~如果你表里的字段是password的那就没你的事啦,此处只针对自定义的密码字段名
使用两个以上需要权限认证的表 比如一个系统中,有管理员和普通用户,他们分别存在两张不同的表(模型)
只需使用上 Config方法动态修改auth.php的model 即可
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
public function adminLogin (Request $request)
{
Config::set('auth.providers.users.model' ,\App\Admin::class);
}
public function userLogin (Request $request)
{
Config::set('auth.providers.users.model' ,\App\User::class);
}
总结 刚开始没看Jwt-auth的源码时,配置起来真的很累,用了各种版本都不,最终发现1.0.0-beta.3可以,又根据github里面的issue,找遍了各种自定义方法加上自己琢磨了一下,才能使得根据自己项目的业务需求尽心修改,当中算是了解了jwt-auth是如何跟laravel自带的auth结合在一起的,收获还算可以。
最后附上tymon大大的github ,虽然被他的拓展折磨的很惨,不过十分感谢大大的贡献.
最后更新时间:2017-07-30 00:50:23
对本文内容有任何意见,建议,疑问etc,均可通过邮箱联系我,逗比博主尽最大努力回复O(∩_∩)O,如需转载(tan90°,不存在的(°Д°)),注明出处即可!