作为程序员一定要保持良好的睡眠,才能好编程

Laravel JWT完整使用图文教程

发布时间:2019-06-09


JWT 全称 JSON Web Tokens ,是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。它的两大使用场景是:认证和数据交换。



第一步、下载composer


# 建议使用1.0以上版本

composer require tymon/jwt-auth 1.*@rc


jwt1.png



第二步、生成文件、修改配置

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider

createk.png

php artisan jwt:secret

create jwt key.png

[root@localhost laravel_base]# php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
Copied File [/vendor/tymon/jwt-auth/config/config.php] To [/config/jwt.php]
Publishing complete.
[root@localhost laravel_base]# php artisan jwt:secret
jwt-auth secret [Jyxc5QkfSSEHNv7XC6wW4YDw0HvBAoApPYfGhnyv5bhylmhyGSw8U44r1FKonviY] set successfully.


jwt2.png



2.1 发布配置文件

# 这条命令会在 config 下增加一个 jwt.php 的配置文件

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

2.2 生成加密密钥

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

php artisan jwt:secret

[root@localhost laravel_base]# php artisan jwt:secret

 This will invalidate all existing tokens. Are you sure you want to override the secret key? (yes/no) [no]:
 > yes

jwt-auth secret [4Zule0z1Fy1BMPbW6MvCmFLhZV9ym9QaWlRqCft1BbMkvjLpmYWQ4akLi7hFSHE3] set successfully.


2.3 更新模型

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

namespace App;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements JWTSubject    # 这里别忘了加
{
    use Notifiable;

    // Rest omitted for brevity

    /**
     * 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 [];
    }}


2.4 注册两个 Facade

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


config/app.php

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


jwt.png


如果你不使用这两个 Facade,你可以使用辅助函数 auth ()


auth () 是一个辅助函数,返回一个 guard,暂时可以看成 Auth Facade。

// 如果你不用 Facade,你可以这么写
auth('api')->refresh();

// 用 JWTAuth Facade
JWTAuth::parseToken()->refresh();



如果你不使用这两个 Facade,你可以使用辅助函数 auth ()


auth () 是一个辅助函数,返回一个 guard,暂时可以看成 Auth Facade。




2.5 修改 auth.php

config/auth.php


'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    'api' => [
        'driver' => 'jwt',      // 原来是 token 改成jwt
        'provider' => 'users',
    ],
],


'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\User::class, //如果你的providers 不是这个User class类,那么需要指定到你的class 文件Z:\laravel_base\config\auth.php
    ],

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


jwt.png




2.6 注册一些路由


注意:在 Laravel 下,route/api.php 中的路由默认都有前缀 api 。

Route::group([
    'prefix' => 'auth'
], function ($router) {
    Route::post('login', 'AuthController@login');
    Route::post('logout', 'AuthController@logout');
    Route::post('refresh', 'AuthController@refresh');
    Route::post('me', 'AuthController@me');
});



2.7 新建控制器类

<?php

namespace App\Http\Controllers;
use App\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Tymon\JWTAuth\JWT;
use Tymon\JWTAuth\JWTAuth;

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

  /**
   * Get a JWT via given credentials.
   *
   * @return \Illuminate\Http\JsonResponse
   */
  public function login() {


    //$data=User::active()->get();
    //
    //return $data;

    //
    //print_r(request()->input('access_token'));
    //
    //exit;

    $credentials = request(['email', 'password']);
    //$credentials = request(['name', 'password']);

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

  /**
   * Get the authenticated User.
   *
   * @return \Illuminate\Http\JsonResponse
   */
  public function me() {
    return response()->json(auth('api')->user());
  }

  /**
   * Log the user out (Invalidate the token).
   *
   * @return \Illuminate\Http\JsonResponse
   */
  public function logout() {
    auth('api')->logout();
    return response()->json(['message' => 'Successfully logged out']);
  }

  /**
   * Refresh a token.
   * 刷新token,如果开启黑名单,以前的token便会失效。
   * 值得注意的是用上面的getToken再获取一次Token并不算做刷新,两次获得的Token是并行的,即两个都可用。
   * @return \Illuminate\Http\JsonResponse
   */
  public function refresh() {
    return $this->respondWithToken(auth('api')->refresh());
  }

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




这里面只有一个login是不验证的,其他方法都是验证token的,


jwt2.png




另外注意一点,且必须包含一个password字段,这是不能丢弃的,否则jwt插件用不了 

password

我们明文传递上来,保存在数据库中的字段是  

\Hash::make('123456');

加密的一个字符串



有了以上的配置几点,看一下运行结果:

看实际调用


login登录

re.png


access_token 成功了


token的类型是 bearer



获取用户

auth/me


re2.png


要访问auth/me必须传递token 串,传递方式 header 头的形式

Authorization   名字


bearer 这是类型,token字符串eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC8xOTIuMTY4LjMxLjEyODo4OTAxXC9hcGlcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNTU5MTc0NDg1LCJleHAiOjE1NTkxNzgwODUsIm5iZiI6MTU1OTE3NDQ4NSwianRpIjoidU9KT3ZMaVBCWkRhYkk2dyIsInN1YiI6MSwicHJ2IjoiODdlMGFmMWVmOWZkMTU4MTJmZGVjOTcxNTNhMTRlMGIwNDc1NDZhYSJ9.ImxOqXPB6J8L-j8x0VqIuLnOcOxpjOJ1d4Tx_i2mAOg





如果字符串传递错误,就会返回  


  <title>Unauthorized</title>

     <div class="flex-center position-ref full-height">

            <div class="code">

                401            </div>


            <div class="message" style="padding: 10px;">

                Unauthorized            </div>

        </div>






auth("api") 会返回  

Tymon\JWTAuth\JWTGuard

这样的一个类,里面有具体的方式和方法  


具体就到这里。




官方手册:

https://jwt-auth.readthedocs.io/en/develop/auth-guard/




其他知识普及:



token的创建 不止一种,接下来介绍token的三种创建方法:

1、基于账号和密码参数

2、基于user模型返回的实例

3、基于users模型中的用户主键id



1)基于账号和密码参数

// 使用辅助函数
$credentials = request(['email', 'password']); 
$token = auth()->attempt($credentials)
// 使用 Facade
$credentials = $request->only('email', 'password');
$token = JWTAuth::attempt($credentials);


2)基于user模型返回的实例

// 使用辅助函数
$user = User::where('username','=','admin')->where('password','=','password123')->first();
$token = auth()->login($user);
// 使用 Facade
$user = User::first();
$token = JWTAuth::fromUser($user);


3)基于users模型中的用户主键id




2.3 token 的解析

a) 解析 token 到对象

只有 Facade 需要这样。

// 把请求发送过来的直接解析到对象

JWTAuth::parseToken();


b) 获取 token 中的 user 信息

// 辅助函数

$user = auth()->user();


// Facade

$user = JWTAuth::parseToken()->authenticate();


c) 获取 token

如果 token 被设置则会返回,否则会尝试使用方法从请求中解析 token ,如果token未被设置或不能解析最终返回false。

// 辅助函数

$token = auth()->getToken();


// Facade

$token = JWTAuth::parseToken()->getToken();


更多方法可以看文章后面的附录。

d) 如果是前端

直接 base64 解码 token 的前两段即可以知道所需的信息。



头部(header)

头部通常由两部分组成:令牌的类型(即JWT)和正在使用的散列算法(如HMAC SHA256 或 RSA.)。

例如:

{
  "alg": "HS256",
  "typ": "JWT"
}

然后用 Base64Url 编码得到头部,即 xxxxx。



载荷(Payload)

载荷中放置了 token 的一些基本信息,以帮助接受它的服务器来理解这个 token,载荷的属性也分三类:预定义(Registered)、公有(public)和私有(private),接下来主要介绍预定义的。

{
  "sub": "1",
  "iss": "http://localhost:8000/auth/login",
  "iat": 1451888119,
  "exp": 1454516119,
  "nbf": 1451888119,
  "jti": "37c107e4609ddbcc9c096ea5ee76c667"
}


这里面的前6个字段都是由JWT的标准所定义的,也就是预定义(Registered claims)的。


sub: 该JWT所面向的用户
iss: 该JWT的签发者
iat(issued at): 在什么时候签发的token
exp(expires): token什么时候过期
nbf(not before):token在此时间之前不能被接收处理
jti:JWT ID为web token提供唯一标识


将上面的 json 进行 Base64Url 编码得到载荷,,即 yyyyy。



签名(Signature)

签名时需要用到前面编码过的两个字符串,如果以 HMACSHA256 加密,就如下:

HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    secret
)

加密后再进行 base64url 编码最后得到的字符串就是 token 的第三部分 zzzzz。

组合便可以得到 token:xxxxx.yyyyy.zzzzz。


a) 载荷设置

载荷信息会在 token 解码时得到,同时越大的数组会生成越长的 token ,所以不建议放太多的数据。同时因为载荷是用 Base64Url 编码,所以相当于明文,因此绝对不能放密码等敏感信息。

$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
// 辅助函数
$token = auth()->claims($customClaims)->attempt($credentials);
// Facade - 1
$token = JWTAuth::claims($customClaims)->attempt($credentials);


b) 载荷解析

从请求中把载荷解析出来。可以去看扩展源代码,里面还有很多的方法。

// 辅助函数

$exp = auth()->payload()->get('exp');
$json = auth()->payload()->toJson();
$array = auth()->payload()->jsonSerialize();
$sub = $array['sub'];
// Facade - 1
$payload = JWTAuth::parseToken()->getPayload();
$payload->get('sub'); // = 123
$payload['jti']; // = 'asfe4fq434asdf'
$payload('exp') // = 123456
$payload->toArray(); // = ['sub' => 123, 'exp' => 123456, 'jti' => 'asfe4fq434asdf'] etc
// Facade - 2
$exp = JWTAuth::parseToken()->getClaim('exp');



4. token 的三个时间

一个 token 一般来说有三个时间属性,其配置都在 config/jwt.php 内。

有效时间

有效时间指的的是你获得 token 后,在多少时间内可以凭这个 token 去获取内容,逾时无效。

// 单位:分钟

'ttl' => env('JWT_TTL', 60)


刷新时间

刷新时间指的是在这个时间内可以凭旧 token 换取一个新 token。例如 token 有效时间为 60 分钟,刷新时间为 20160 分钟,在 60 分钟内可以通过这个 token 获取新 token,但是超过 60 分钟是不可以的,然后你可以一直循环获取,直到总时间超过 20160 分钟,不能再获取。

// 单位:分钟

'refresh_ttl' => env('JWT_REFRESH_TTL', 20160)


宽限时间

宽限时间是为了解决并发请求的问题,假如宽限时间为 0s ,那么在新旧 token 交接的时候,并发请求就会出错,所以需要设定一个宽限时间,在宽限时间内,旧 token 仍然能够正常使用。

// 宽限时间需要开启黑名单(默认是开启的),黑名单保证过期token不可再用,最好打开

'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true)


// 设定宽限时间,单位:秒

'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 60)



JWT 完整使用详解

https://www.jianshu.com/p/7bb8fb395864


这是一个完整版的jwt非常详细 ,推荐查看:

https://www.jianshu.com/p/9e95a5f8ac4a



其他逻辑,https://learnku.com/articles/6216/laravel-uses-jwt-to-implement-api-auth-to-build-user-authorization-interfaces

https://laravelacademy.org/post/3640.html




参考文章:

https://learnku.com/articles/10885/full-use-of-jwt


https://www.jianshu.com/p/b89df38e886b


https://blog.csdn.net/qq_36514588/article/details/82186617


https://learnku.com/articles/10889/detailed-implementation-of-jwt-extensions