laravel - json web token

1006 查看

Json web token

简称JWT,一个健壮令牌标准-RFC7519
JWT由三部分组成:

  1. header

  2. payload

  3. VERIFY SIGNATURE

header&payload用base64编码,中间用.隔开,重要的是VERIFY SIGNATURE,使用HMAC SHA-256算法生成摘要,具体为

#伪代码
HMACSHA256(
  base64encode(header) + "." + base64Encode(payload) + '.' +secret
)

php中hs256算法:

//algo指定为sha256,数据放data,screat为key
string hash_hmac ( string $algo , string $data , string $key [, bool $raw_output = false ] )

secret存服务器端,别泄露了。具体了解可参考:JWT.io


Laravel + jwt

这里Token生成的算法及验证就确定下来,和laravel 5.2 搭配使用如下:

基本配置

1.安装:composer require tymon/jwt-auth 0.5.*
2.config/app.php中:

  1. provider增加

    Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class
  2. aliases 增加

    'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
    'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class
    

3.发布相应配置文件

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

执行完命令后在config/下会新增jwt配置文件jwt.php:

//file:jwt.php
    /*
    |--------------------------------------------------------------------------
    | JWT Authentication Secret (进行加密算法的时候的secret,这里32位)
    |--------------------------------------------------------------------------
    */

    'secret' => env('JWT_SECRET', 'ZD23ADFVKSKSDFSDJFKDFDFVsafaa12a'),

    /*
    |--------------------------------------------------------------------------
    | JWT time to live (token有效期,单位分钟)
    |--------------------------------------------------------------------------
    | Defaults to 1 hour
    | 1 hour = 60 minutes
    */

    'ttl' => 60,

    /*
    |--------------------------------------------------------------------------
    | Refresh time to live (刷新token时间,单位分钟)
    |--------------------------------------------------------------------------
    | Defaults to 2 weeks
    | 2 weeks = 20160 minute
    */

    'refresh_ttl' => 20160,

    /*
    |--------------------------------------------------------------------------
    | JWT hashing algorithm (token签名算法)
    |--------------------------------------------------------------------------
    */

    'algo' => 'HS256',

    /*
    |--------------------------------------------------------------------------
    | User Model namespace (指向User模型的命名空间路径)
    |--------------------------------------------------------------------------
    */

    'user' => 'App\Models\User',

    /*
    |--------------------------------------------------------------------------
    | User identifier (用于从token的sub中获取用户)
    |--------------------------------------------------------------------------
    |
    */

    'identifier' => 'id',

    /*
    |--------------------------------------------------------------------------
    | Required Claims (必须出现在token的payload中的选项,否则会抛出TokenInvalidException异常) 
    |--------------------------------------------------------------------------
    |
    */

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

    /*
    |--------------------------------------------------------------------------
    | Blacklist Enabled (如果该选项被设置为false,那么我们将不能废止token,即使我们刷新了token,前一个token仍然有效)
    |--------------------------------------------------------------------------
    */

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

    /*
    |--------------------------------------------------------------------------
    | Providers (完成各种任务的具体实现,有需求的情况写可重写)
    |--------------------------------------------------------------------------
    */

    'providers' => [


        'user' => 'Tymon\JWTAuth\Providers\User\EloquentUserAdapter',

        'jwt' => 'Tymon\JWTAuth\Providers\JWT\NamshiAdapter',

        'auth' => 'Tymon\JWTAuth\Providers\Auth\IlluminateAuthAdapter',

        'storage' => 'Tymon\JWTAuth\Providers\Storage\IlluminateCacheAdapter',

    ],

];

4.最后生成密钥secret,写入config/jwt.phpJWT_SECRET的value

php artisan jwt:generate

生成token

1.控制器中创建token,使用orm获取用户对象:

$userObj = User::where('email',$username)->first();
$res['token'] = JWTAuth::fromUser($userObj);

2.客户端获取到token,可写入localstorage,正常会话周期每次请求带上,token放入header中:Authorization : Bearer {token},这里的bearer有持票人的意思,token两边的{}必须有。

在apache下会有header被丢弃的情况,apache下得写配置

RewriteEngine On
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]

验证

因为接口多,放到路由群组用中间件过滤(中间件姿势如使用不正确请指出):
1.执行php artisan make:middleware verifyToken,在app\Http\Middleware\verifyToken.php会生成verifyToken前置中间件,填充代码如下:

namespace App\Http\Middleware;

use Closure;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;

class verifyToken
{
    /**
     * 请求前置中间件
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        try {
            if (!JWTAuth::parseToken()->authenticate()) {
                return response()->json(['user_not_found'], 404);
            }
        } catch (TokenExpiredException $e) {
            //toke失效,response code可自定义
            return response()->json(['token_expired'], 405);
        } catch (TokenInvalidException $e) {
            return response()->json(['token_invalid'], $e->getStatusCode());
        } catch (JWTException $e) {
            return response()->json(['token_absent'], $e->getStatusCode());
        }
        return $next($request);
    }
}

2.app\Http\Kernel.php中在routeMiddleware处注册中间件,

'verifyToken' => 'App\Http\Middleware\verifyToken'

3.路由群组中使用:

//群组中中间件可以用多个,用数组传入即可
//这里的fetchResponse是后置中间件,用于包装供cors跨域的几个header
Route::group(['middleware' => ['fetchResponse','verifyToken']],function(){
    Route::match(['POST','OPTIONS'],'someDo','someController@someFunc');
})

4.客户端可根据响应码不同进行处理。完。

参考链接:
1.Github jtw-auth
2.Laravel 5 中使用 JWT(Json Web Token) 实现基于API的用户认证 - laravel学院
3.JWT安全问题-翻译 2015年