不说前面的安装laravel了,
通过shell进入项目根目录,运行以下命令进行安装jwt-auth
composer require tymon/jwt-auth
将下面这行添加至 config/app.php 文件 providers 数组中
'providers' => [
...
TymonJWTAuthProvidersLaravelServiceProvider::class,
]
在shell中运行以下命令,在config目录下会生成jwt.php配置文件。
php artisan vendor:publish --provider="TymonJWTAuthProvidersLaravelServiceProvider"
运行以下命令,会生成密钥,在.env文件中可以看到新增JWT_SECRET=secret。
php artisan jwt:secret
在config/auth.php文件中,将guards/driver更新为jwt,如下:
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
如果使用jwt-auth作为用户认证,则需要修改User模型。在app/User.php
<?php
namespace App;
use TymonJWTAuthContractsJWTSubject;
use IlluminateNotificationsNotifiable;
use IlluminateFoundationAuthUser as Authenticatable;
class User extends Authenticatable implements JWTSubject
{
use Notifiable;
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}
}
执行如下命令以新建一个中间件:
php artisan make:middleware Token
运行后,会在app/http/middleware中看到Token.php文件,需要注意的是记得在app/http/Kernel.php添加如下代码:
protected $routeMiddleware = [
...
'refreshtoken' => AppHttpMiddlewareToken::class,
];
中间件代码如下:(Token.php)
<?php
namespace AppHttpMiddleware;
use Auth;
use Closure;
use TymonJWTAuthExceptionsJWTException;
use TymonJWTAuthHttpMiddlewareBaseMiddleware;
use TymonJWTAuthExceptionsTokenExpiredException;
use SymfonyComponentHttpKernelExceptionUnauthorizedHttpException;
// 注意,我们要继承的是 jwt 的 BaseMiddleware
class RefreshToken extends BaseMiddleware
{
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);
}
}
需要更新一下 app/Exceptions/Handler.php 中的 render方法,自定义处理一些异常。Handler.php
<?php
namespace AppExceptions;
use IlluminateFoundationExceptionsHandler as ExceptionHandler;
use Throwable;
use SymfonyComponentHttpKernelExceptionUnauthorizedHttpException;
use IlluminateValidationValidationException;
class Handler extends ExceptionHandler
{
protected $dontReport = [
//
];
protected $dontFlash = [
'password',
'password_confirmation',
];
public function report(Throwable $exception)
{
parent::report($exception);
}
public function render($request, Throwable $exception)
{
// 参数验证错误的异常,我们需要返回 400 的 http code 和一句错误信息
if ($exception instanceof ValidationException) {
return response(['error' => array_first(array_collapse($exception->errors()))], 400);
}
if ($exception instanceof UnauthorizedHttpException) {
$preException = $exception->getPrevious();
if ($preException instanceof
TymonJWTAuthExceptionsTokenExpiredException) {
return response()->json(['error' => 'TOKEN已过期!','code' => 406]);
} else if ($preException instanceof
TymonJWTAuthExceptionsTokenInvalidException) {
return response()->json(['error' => 'TOKEN无效!','code' => 406]);
} else if ($preException instanceof
TymonJWTAuthExceptionsTokenBlacklistedException) {
return response()->json(['error' => 'TOKEN已退出!','code' => 406]);
}
if ($exception->getMessage() === 'Token not provided') {
return response()->json(['error' => 'Token为空!','code' => 406]);
}
}
if ($exception instanceof
TymonJWTAuthExceptionsTokenExpiredException) {
return response()->json(['error' => 'TOKEN已过期!','code' => 406]);
} else if ($exception instanceof
TymonJWTAuthExceptionsTokenInvalidException) {
return response()->json(['error' => 'TOKEN无效!','code' => 406]);
} else if ($exception instanceof
TymonJWTAuthExceptionsTokenBlacklistedException) {
return response()->json(['error' => 'TOKEN已退出!','code' => 406]);
}
if ($exception->getMessage() === 'Token not provided') {
return response()->json(['error' => 'Token为空!','code' => 406]);
}
}
}
创建一个控制器
php artisan make:controller IndexController
控制可以写
<?php
namespace AppHttpControllersApi;
use AppHttpControllersController;
use IlluminateHttpRequest;
class IndexController extends Controller
{
public function __construct()
{
$this->middleware('jwt.auth', ['except' => ['login']]);
// 另外关于上面的中间件,官方文档写的是『auth:api』
// 但是我推荐用 『jwt.auth』,效果是一样的,但是有更加丰富的报错信息返回
}
public function index(){
return auth()->user();
}
public function login()
{
$credentials = request(['email', 'password']);
if (! $token = auth('api')->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
public function logout()
{
auth('api')->logout();
return response()->json(['message' => '退出成功!']);
}
public function refresh()
{
return $this->respondWithToken(auth('api')->refresh());
}
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
]);
}
}
测试一下
Route::middleware('jwt.auth::api')->group(function(){ //控制器中也有鉴权
Route::get('/user', function (Request $request) {
return '牛逼';
});
Route::get('/logout', 'ApiIndexController@logout');
Route::get('/index', 'ApiIndexController@index');
});
Route::get('/login', 'ApiIndexController@login');
在数据库中直接添加数据,密码是通过bcrypt加密,如 bcrypt(‘123456’),将加密的数据添加到表中
php artisan tinker
>>> namespace AppModels;
>>> User::create(['name'=>'Test','email'=> 1762332881,'password'=> bcrypt('123456')]);
有可能会报错
在AppModelsUser.php
protected $guarded = [];