基于 JWT 实现 Laravel API 认证 [ Laravel 5.5 ]

JWT

JWT 是 JSON Web Token 的缩写,是一个非常轻巧的规范,这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。

JWT 由头部(header)、载荷(payload)与签名(signature)组成,一个 JWT 类似下面这样:

{
    "typ":"JWT",
    "alg":"HS256"
}
{
    "iss":"http://larabbs.test",
    "iat":1515733500,
    "exp":1515737100,
    "nbf":1515733500,
    "jti":"c3U4VevxG2ZA1qhT",
    "sub":1,
    "prv":"23bd5c8949f600adb39e701c400872db7a5976f7"
}
signature

安装 jwt-auth

jwt-authLaravellumen 的JWT组件,首先来安装一下,

  • Laravel 5.5 的适配版本为 1.0.0-rc.2
  • Laravel 5.7 的适配版本为 1.0.0-rc.3
$ composer require tymon/jwt-auth:1.0.0-rc.2

安装完成后,我们需要设置一下 JWT 的 secret,这个 secret 很重要,用于最后的签名,更换这个secret 会导致之前生成的所有 token 无效。

$ php artisan jwt:secret

可以看到在 .env 文件中,增加了一行 JWT_SECRET

修改 config/auth.php,将 api guard 的 driver 改为 jwt

config/auth.php

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

    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],
.
.
.

更改 User Model,让User 支持 jwt-auth

user 模型需要继承 Tymon\JWTAuth\Contracts\JWTSubject接口,并实现接口的两个方法 getJWTIdentifier()getJWTCustomClaims()

app\Models\User.php

<?php

namespace App\Models;

use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Auth;
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable implements JWTSubject
.
.
.
    // Rest omitted for brevity

    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims()
    {
        return [];
    }
}

使用 jwt

下面就可以使用了,跟 laravel csrf-token 一样我们在每个模版meta中注册 api-token

resources/views/layouts/app.blade.php

{{-- CSRF Token --}}
<meta name="csrf-token" content="{{ csrf_token() }}">

@auth
{{-- JWT Token --}}
<meta name="jwt-token" content="{{ 'Bearer '.JWTAuth::fromUser(Auth::user()) }}">
@endauth

每次ajax请求的时候获取api-token放置到header中就可以了。

resources/assets/js/bootstrap.js

window.axios = require('axios');

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

let jwt_token = document.head.querySelector('meta[name="jwt-token"]');
if(jwt_token) {
    window.axios.defaults.headers.common['Authorization'] = jwt_token.content;
}

laravel 封装 登录,退出 Api 接口

路由

routes/api.php

.
.
.
// 游客可以访问的接口
Route::group([
    'namespace'=> 'Api',
], function ($router){
    // 登录
    $router->post('authorizations', 'AuthorizationsController@login')
        ->name('api.authorizations.login');
});

// 需要 token 验证的接口
Route::group([
    'namespace'=> 'Api',
    'middleware'=> 'auth:api',
], function ($router){
    // 刷新token
    $router->put('authorizations/refresh', 'AuthorizationsController@refresh')
        ->name('api.authorizations.refresh');
    // 删除token
    $router->delete('authorizations/logout', 'AuthorizationsController@logout')
        ->name('api.authorizations.logout');
    // 获取当前登录用户信息
    $router->get('me', 'AuthorizationsController@me')->name('api.me.show');
});

控制器

app/Http/Controllers/Api/AuthorizationsController.php

<?php

namespace App\Http\Controllers\Api;

use App\Http\Requests\Api\AuthorizationRequest;
use Illuminate\Http\Request;
# use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;

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

    // api 登录
    public function login(AuthorizationRequest $request)
    {
        $credentials = [
            'email' => $request->email,
            'password' => $request->password,
        ];

        if (! $token = Auth::guard('api')->attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
            // return $this->errorUnauthorized('用户名或密码错误');
        }

        return $this->respondWithToken($token);
    }

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

    // 删除 token
    public function logout()
    {
        Auth::guard('api')->logout();
        // return $this->noContent();
        return response()->json(['message' => 'Successfully logged out']);
    }

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

    // 响应 token
    protected function respondWithToken($token)
    {
        return response([
            'access_token' => $token,
            'token_type' => 'Bearer',
            'expires_in' => Auth::guard('api')->factory()->getTTL() * 60
        ], 201);
    }
}
讨论数量: 0

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!