Laravel 5.5 使用 Jwt-Auth 实现 API 用户认证
Posted jxl1996
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Laravel 5.5 使用 Jwt-Auth 实现 API 用户认证相关的知识,希望对你有一定的参考价值。
安装
composer require tymon/jwt-auth 1.0.0-rc.1
配置
添加服务提供者
‘providers‘ => [ TymonJWTAuthProvidersLaravelServiceProvider::class, ]
发布配置文件
php artisan vendor:publish --provider="TymonJWTAuthProvidersLaravelServiceProvider"
生成密钥
php artisan jwt:secret
配置 Auth guard
‘guards‘ => [ ‘api‘ => [ ‘driver‘ => ‘jwt‘, ‘provider‘ => ‘users‘, ], ],
更改 Model
实现TymonJWTAuthContractsJWTSubject接口,编写
getJWTIdentifier和getJWTCustomClaims方法。
<?php namespace App; use TymonJWTAuthContractsJWTSubject; use IlluminateNotificationsNotifiable; use IlluminateFoundationAuthUser 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 []; } }
配置项详解
jwt.php
<?php return [ /* |-------------------------------------------------------------------------- | JWT Authentication Secret |-------------------------------------------------------------------------- | | 用于加密生成 token 的 secret | */ ‘secret‘ => 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‘ => TymonJWTAuthProvidersJWTNamshi::class, /* |-------------------------------------------------------------------------- | Authentication Provider |-------------------------------------------------------------------------- | | 指定用于对用户进行身份验证的提供程序。 | */ ‘auth‘ => TymonJWTAuthProvidersAuthIlluminate::class, /* |-------------------------------------------------------------------------- | Storage Provider |-------------------------------------------------------------------------- | | 指定用于在黑名单中存储标记的提供程序。 | */ ‘storage‘ => TymonJWTAuthProvidersStorageIlluminate::class, ], ];
自定义认证中间件
先来说明一下我想要达成的效果,我希望用户提供账号密码前来登录。如果登录成功,那么我会给前端颁发一个 access _token ,设置在 header
中以请求需要用户认证的路由。
同时我希望如果用户的令牌如果过期了,可以暂时通过此次请求,并在此次请求中刷新该用户的 access _token,最后在响应头中将新的 access _token 返回给前端,这样子可以无痛的刷新 access _token ,用户可以获得一个很良好的体验,所以开始动手写代码。
执行如下命令以新建一个中间件:
php artisan make:middleware RefreshToken
中间件代码如下:
RefreshToken.php
<?php namespace AppHttpMiddleware; use Auth; use Closure; use TymonJWTAuthExceptionsJWTException; use TymonJWTAuthHttpMiddlewareBaseMiddleware; use TymonJWTAuthExceptionsTokenExpiredException; use SymfonyComponentHttpKernelExceptionUnauthorizedHttpException; // 注意,我们要继承的是 jwt 的 BaseMiddleware class RefreshToken extends BaseMiddleware { /** * Handle an incoming request. * * @param IlluminateHttpRequest $request * @param Closure $next * * @throws SymfonyComponentHttpKernelExceptionUnauthorizedHttpException * * @return mixed */ public function handle($request, Closure $next) { // 检查此次请求中是否带有 token,如果没有则抛出异常。 $this->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); } }
更新异常处理的 Handler
由于我们构建的是 api
服务,所以我们需要更新一下 app/Exceptions/Handler.php
中的 render
方法,自定义处理一些异常。
Handler.php
<?php namespace AppExceptions; use Exception; use IlluminateFoundationExceptionsHandler as ExceptionHandler; use IlluminateValidationValidationException; use SymfonyComponentHttpKernelExceptionUnauthorizedHttpException; class Handler extends ExceptionHandler { ... /** * Render an exception into an HTTP response. * * @param IlluminateHttpRequest $request * @param Exception $exception * @return IlluminateHttpResponse */ public function render($request, Exception $exception) { // 参数验证错误的异常,我们需要返回 400 的 http code 和一句错误信息 if ($exception instanceof ValidationException) { return response([‘error‘ => array_first(array_collapse($exception->errors()))], 400); } // 用户认证的异常,我们需要返回 401 的 http code 和错误信息 if ($exception instanceof UnauthorizedHttpException) { return response($exception->getMessage(), 401); } return parent::render($request, $exception); } }
测试
// 使用 Auth 登录用户,如果登录成功,则返回 201 的 code 和 token,如果登录失败则返回 return ($token = Auth::guard(‘api‘)->attempt($request->only(‘email‘,‘password‘))) ? response([‘token‘ => ‘bearer ‘ . JWTAuth::fromUser(Auth::guard(‘api‘)->user())], 201) : response([‘error‘ => ‘账号或密码错误‘], 400); }
参考文档
https://laravelacademy.org/post/9794.html
https://www.jianshu.com/p/9e95a5f8ac4a
以上是关于Laravel 5.5 使用 Jwt-Auth 实现 API 用户认证的主要内容,如果未能解决你的问题,请参考以下文章