Laravel 5.5 使用 Jwt-Auth 实现 API 用户认证、刷新令牌(一)

需求:

新项目,采用前后端分离的模式,前端使用 Vue.js,后端使用 Laravel 5.5构建 Api 服务,用户认证的包使用 jwt-auth 。本次博客会分4步完成,

  1. Laravel 5.5 使用 Jwt-Auth 实现 API 用户认证、刷新令牌(一)。
  2. Laravel–CORS 扩展包完美解决前后端分离应用跨域请求
  3. vue+axios 拦截器实现统一token
  4. Laravel 5.5 使用 Jwt-Auth 实现 API多 用户、多字段认证、刷新令牌(二)

概述:

JWT(JSON Web Token)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。
更详细讲解地址:https://laravelacademy.org/post/3640.html

安装

1.个人使用laravel5.5 使用composer require tymon/jwt-authphp artisan jwt:generate报错,所以使用以下命令

composer require tymon/jwt-auth:dev-develop --prefer-source

2.安装完成后在配置文件 config/app.php 中注册服务提供者和别名:

'providers' => [
    Tymon\JWTAuth\Providers\LaravelServiceProvider::class
]

'aliases' => [
    'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
]

3.发布配置文件

在你项目根目录运行如下命令发布生成config\ jwt.php 的配置文件:

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

4.在发布的配置中生成key:

此命令会在你的 .env 文件中新增一行 JWT_SECRET=secret。

php artisan jwt:secret

5.配置 Auth guard

在 config/auth.php 文件中,你需要将 guards/driver 更新为 jwt:

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],
    ],

6.更改 Model
如果需要使用 jwt-auth 作为用户认证,我们需要对我们的 User 模型进行修改,实现一个接口,变更后的 User 模型如下:

getKey();
    }

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

}

7.配置项详解

文件位置:config/jwt.php

 env('JWT_SECRET'),

    /*
    |--------------------------------------------------------------------------
    | JWT Authentication Keys
    |--------------------------------------------------------------------------
    |
    | 如果你在 .env 文件中定义了 JWT_SECRET 的随机字符串
    | 那么 jwt 将会使用 对称算法 来生成 token
    | 如果你没有定有,那么jwt 将会使用如下配置的公钥和私钥来生成 token
    |
    */

    'keys' => [

        /*
        |--------------------------------------------------------------------------
        | Public Key
        |--------------------------------------------------------------------------
        |
        | 公钥
        |
        */

        'public' => env('JWT_PUBLIC_KEY'),

        /*
        |--------------------------------------------------------------------------
        | Private Key
        |--------------------------------------------------------------------------
        |
        | 私钥
        |
        */

        'private' => env('JWT_PRIVATE_KEY'),

        /*
        |--------------------------------------------------------------------------
        | Passphrase
        |--------------------------------------------------------------------------
        |
        | 私钥的密码。 如果没有设置,可以为 null。
        |
        */

        'passphrase' => env('JWT_PASSPHRASE'),

    ],

    /*
    |--------------------------------------------------------------------------
    | JWT time to live
    |--------------------------------------------------------------------------
    |
    | 指定 access_token 有效的时间长度(以分钟为单位),默认为1小时,您也可以将其设置为空,以产生永不过期的标记
    |
    */

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

    /*
    |--------------------------------------------------------------------------
    | Refresh time to live
    |--------------------------------------------------------------------------
    |
    | 指定 access_token 可刷新的时间长度(以分钟为单位)。默认的时间为 2 周。
    | 大概意思就是如果用户有一个 access_token,那么他可以带着他的 access_token 
    | 过来领取新的 access_token,直到 2 周的时间后,他便无法继续刷新了,需要重新登录。
    |
    */

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

    /*
    |--------------------------------------------------------------------------
    | JWT hashing algorithm
    |--------------------------------------------------------------------------
    |
    | 指定将用于对令牌进行签名的散列算法。
    |
    */

    'algo' => env('JWT_ALGO', 'HS256'),

    /*
    |--------------------------------------------------------------------------
    | Required Claims
    |--------------------------------------------------------------------------
    |
    | 指定必须存在于任何令牌中的声明。
    | 
    |
    */

    'required_claims' => [
        'iss',
        'iat',
        'exp',
        'nbf',
        'sub',
        'jti',
    ],

    /*
    |--------------------------------------------------------------------------
    | Persistent Claims
    |--------------------------------------------------------------------------
    |
    | 指定在刷新令牌时要保留的声明密钥。
    |
    */

    'persistent_claims' => [
        // 'foo',
        // 'bar',
    ],

    /*
    |--------------------------------------------------------------------------
    | Blacklist Enabled
    |--------------------------------------------------------------------------
    |
    | 为了使令牌无效,您必须启用黑名单。
    | 如果您不想或不需要此功能,请将其设置为 false。
    |
    */

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

    /*
    | -------------------------------------------------------------------------
    | Blacklist Grace Period
    | -------------------------------------------------------------------------
    |
    | 当多个并发请求使用相同的JWT进行时,
    | 由于 access_token 的刷新 ,其中一些可能会失败
    | 以秒为单位设置请求时间以防止并发的请求失败。
    |
    */

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

    /*
    |--------------------------------------------------------------------------
    | Providers
    |--------------------------------------------------------------------------
    |
    | 指定整个包中使用的各种提供程序。
    |
    */

    'providers' => [

        /*
        |--------------------------------------------------------------------------
        | JWT Provider
        |--------------------------------------------------------------------------
        |
        | 指定用于创建和解码令牌的提供程序。
        |
        */

        'jwt' => Tymon\JWTAuth\Providers\JWT\Namshi::class,

        /*
        |--------------------------------------------------------------------------
        | Authentication Provider
        |--------------------------------------------------------------------------
        |
        | 指定用于对用户进行身份验证的提供程序。
        |
        */

        'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,

        /*
        |--------------------------------------------------------------------------
        | Storage Provider
        |--------------------------------------------------------------------------
        |
        | 指定用于在黑名单中存储标记的提供程序。
        |
        */

        'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,

    ],

];

8.自定义认证中间件
实现效果:提供账号密码前来登录。如果登录成功,那么我会给前端颁发一个 access _token ,设置在 header 中以请求需要用户认证的路由。
如果用户的令牌如果过期了,可以暂时通过此次请求,并在此次请求中刷新该用户的 access _token,最后在响应头中将新的 access _token 返回给前端,这样子可以无痛的刷新 access _token ,用户可以获得一个很良好的体验,所以开始动手写代码。

执行如下命令以新建一个中间件:

php artisan make:middleware RefreshToken

中间件app\Http\Middleware\RefreshToken.php代码如下:

checkForToken($request);

        // 使用 try 包裹,以捕捉 token 过期所抛出的 TokenExpiredException  异常
        try {
            // 检测用户的登录状态,如果正常则通过
            if ($this->auth->parseToken()->authenticate()) {
                return $next($request);
            }
            throw new UnauthorizedHttpException('jwt-auth', '未登录');
        } catch (TokenExpiredException $exception) {
            // 此处捕获到了 token 过期所抛出的 TokenExpiredException 异常,我们在这里需要做的是刷新该用户的 token 并将它添加到响应头中
            try {
                // 刷新用户的 token
                $token = $this->auth->refresh();
                // 使用一次性登录以保证此次请求的成功
                Auth::guard('api')->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
            } catch (JWTException $exception) {
                // 如果捕获到此异常,即代表 refresh 也过期了,用户无法刷新令牌,需要重新登录。
                throw new UnauthorizedHttpException('jwt-auth', $exception->getMessage());
            }
        }

        // 在响应头中返回新的 token
        return $this->setAuthenticationHeader($next($request), $token);
    }
}

然后我们注册一下中间件,文件位置app\Http\Kernel.php在路由处会使用

protected $routeMiddleware = [
        'refresh' => \App\Http\Middleware\RefreshToken::class,
]

9.更新异常处理的 Handler

由于我们构建的是 api 服务,所以我们需要更新一下 app/Exceptions/Handler.php 中的 render方法,自定义处理一些异常。

 array_first(array_collapse($exception->errors()))], 400);
       }
       // 用户认证的异常,我们需要返回 401 的 http code 和错误信息
       if ($exception instanceof UnauthorizedHttpException) {
           return response($exception->getMessage(), 401);
       }

       return parent::render($request, $exception);
   }
}

更新完此方法后,我们上面自定义的中间件里抛出的异常和我们下面参数验证错误抛出的异常都会被转为指定的格式抛出。
10. 路由写法

我们在 routes/api.php 路由文件中新增几条路由来测试一下了:

Route::group(function($router) {
    $router->post('login', 'AuthController@login');
    $router->post('logout', 'AuthController@logout');

});

Route::middleware('refresh')->group(function($router) {
    $router->get('profile','UserController@profile');
});

11.执行如下命令以新建一个中间件:

php artisan make:controller AuthController

打开此控制器,写入如下内容

 [
                'required',
                'exists:users',
            ],
            'password' => 'required|string|min:6|max:20',
         ];

        // 验证参数,如果验证失败,则会抛出 ValidationException 的异常
        $params = $this->validate($request, $rules);

       // 使用 Auth 登录用户,如果登录成功,则返回 201 的 code 和 token,如果登录失败则返回
        return ($token = Auth::guard('api')->attempt($params))
            ? response(['token' => 'bearer ' . $token], 201)
            : response(['error' => '账号或密码错误'], 400);
    }

    /**
     * 处理用户登出逻辑
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        Auth::guard('api')->logout();

        return response(['message' => '退出成功']);
    }
}

好了接下来可以去测试了,我们看一下测试结果
Laravel 5.5 使用 Jwt-Auth 实现 API 用户认证、刷新令牌(一)_第1张图片
Laravel 5.5 使用 Jwt-Auth 实现 API 用户认证、刷新令牌(一)_第2张图片

首先感谢原有博主:https://www.jianshu.com/p/9e95a5f8ac4a
本人不才,稍稍抄袭和完善一波,如果博主感觉有问题可以联系本人,感觉这篇文章基本解决我们的Jwt-Auth认证,如此此篇文章对大家有帮助,我愿意以后有时间多多更新,谢谢您的欣赏和参考过的博客

你可能感兴趣的:(laravel)