laravel firebase/php-jwt token验证

1、说明

这里的jwt和之前另外一篇的jwt有些不一样,之前的是基于用户的接口验证,即需要接口登录,此jwt则是基于一个唯一标识,如移动设备唯一ID或手机号码等能够作为唯一标识的数据信息,通过它来完成token签发、验证。

2、安装

composer require firebase/php-jwt

3、使用

3.1、签发token

public function createToken()
{
    $key = '344'; //key,唯一标识
    $time = time(); //当前时间
    $token = [
        'iat' => $time, //签发时间
        'nbf' => $time , //(Not Before):某个时间点后才能访问,比如设置time+30,表示当前时间30秒后才能使用
        'exp' => $time+7200, //过期时间,这里设置2个小时
        'data' => [ //自定义信息,不要定义敏感信息
            'device_id' => 'asdfghj',
        ]
    ];
    $token = JWT::encode($token, $key,'HS256'); //签发token
    $data = ['error'=>0,'mgs'=>'success','data'=>['token'=>$token]];
    return json_encode($data);
}

3.2、验证token

// $token:签发的token
public function verifyToken($token)
{
    $key = '344'; //key要和签发的时候一样,唯一标识
    try {
        JWT::$leeway = 60;//当前时间减去60,把时间留点余地
        $decoded = JWT::decode($token, $key, ['HS256']); //HS256方式,这里要和签发的时候对应
        $arr = (array)$decoded;
        print_r($arr);
    } catch(\Firebase\JWT\SignatureInvalidException $e) {  //签名不正确
        echo $e->getMessage();
    }catch(\Firebase\JWT\BeforeValidException $e) {  // 签名在某个时间点之后才能用
        echo $e->getMessage();
    }catch(\Firebase\JWT\ExpiredException $e) {  // token过期
        echo $e->getMessage();
    }catch(Exception $e) {  //其他错误
        echo $e->getMessage();
    }
}

4、综合运用

4.1、使用场景

项目中app接口需要接口验证,又不要登录,app端能够提供1个唯一标识设备唯一ID,通过这个ID生成token,并通过这个ID和token来完成接口验证。

4.2、使用

①、安装

composer require firebase/php-jwt

②、封装2个方法

1个用来生成token,1个用来刷新token

key:设备唯一ID的加密字符串,接口请求时也需要提供,验证的时候需要用到

/**
     * 生成并返回token
     * @return bool|string
     */
    public function getToken($param)
    {
        $deviceId = isset($param['device_id']) ? $param['device_id'] : '';
        //device_id做为唯一标识
        
        if(empty($deviceId)){
            return $this->responseInvalidParams('device_id is empty');
        }
        $key = encrypt($deviceId); //key
        $time = time(); //当前时间
        $overtime = 7200;
        $token = [
            'iat' => $time, //签发时间
            'nbf' => $time , //(Not Before):某个时间点后才能访问,比如设置time+30,表示当前时间30秒后才能使用
            'exp' => $time+$overtime, //过期时间,这里设置2个小时
        ];
        if(!$token = JWT::encode($token, $key,'HS256')){
            \Log::error('app api operation failed:key='.$key.', token='.$token);
            return  response()->json(['error'=>1,'msg'=>'operation failed','data'=>[]]);
        }
        return  response()->json(['error'=>0,'msg'=>'ok','data'=>['key'=>$key,'token'=>$token,'expire'=>$overtime]]);
    }

    /**
     * 重新生成并返回token
     * @return bool|string
     */
    public function refreshToken($key)
    {
        if(empty($key)){
            return $this->responseInvalidParams('key is empty');
        }

        $newKey = encrypt(decrypt($key));//重新生成key
        $time = time(); //当前时间
        $overtime = 7200;
        $token = [
            'iat' => $time, //签发时间
            'nbf' => $time , //(Not Before):某个时间点后才能访问,比如设置time+30,表示当前时间30秒后才能使用
            'exp' => $time+$overtime, //过期时间,这里设置2个小时
        ];
        if(!$token = JWT::encode($token, $newKey,'HS256')){
            \Log::error('app api operation failed:key='.$newKey.', token='.$token);
            return  response()->json(['error'=>1,'msg'=>'operation failed','data'=>[]]);
        }

        return  response()->json(['error'=>0,'msg'=>'ok','data'=>['key'=>$key,'token'=>$token,'expire'=>$overtime]]);
    }

③、定义1个中间件

通过中间件,让接口每次请求是都去验证token

php artisan make:middleware VerifySign //生成中间件

VerifySign:验证token

<?php

namespace App\Http\Middleware;

use Closure;
use Exception;
use Firebase\JWT\JWT;

class VerifySign
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $result = ['error'=>0,'msg'=>'','data'=>[]];

        $key = $request->header('key');
        $token = $request->header('token');
        if(empty($key) || empty($token)){
            $result['error'] = 403;
            $result['msg'] = 'headers param error';
            \Log::error('app api headers param error:key='.$key.', token='.$token);
            return response()->json($result);
        }

        try {
            JWT::$leeway = 60;//当前时间减去60,把时间留点余地
            if(!$rs = JWT::decode($token, $key, ['HS256'])){//HS256方式,这里要和签发的时候对应
                $result['error'] = 403;
                $result['msg'] = 'validation failure';
                \Log::error('app api validation failure:key='.$key.', token='.$token);
                return response()->json($result);
            }
        } catch(\Firebase\JWT\SignatureInvalidException $e) {  //签名不正确
            $error = $e->getMessage();
        }catch(\Firebase\JWT\BeforeValidException $e) {  // 签名在某个时间点之后才能用
            $error =  $e->getMessage();
        }catch(\Firebase\JWT\ExpiredException $e) {  // token过期
            $error =  $e->getMessage();
        }catch(Exception $e) {  //其他错误
            $error =  $e->getMessage();
        }

        if(!empty($error)){
            $result['error'] = 403;
            $result['msg'] = $error;
            \Log::error('app api error:'.$error);
            return response()->json($result);
        }

        return $next($request);
    }
}

④、app/Http/Kernel.php 应用的路由中间件列表

protected $routeMiddleware = [
	//...
	'app.sign' => \App\Http\Middleware\VerifySign::class,
];

⑤、定义路由

routes/api.php

Route::get('auth/token', 'App\System\UserController@getToken');//获取token

//需要通过验证token的路由
Route::group(['namespace' => 'App','middleware' => ['app.sign']], function () {
    Route::get('/auth/refresh/token', 'System\UserController@refreshToken');//刷新token
    Route::get('/home/index', 'Home\IndexController@index');//首页
    //...
});		

⑥、控制器中

调用前面封装好的操作token方法

//获取token
public function getToken(Request $request)
{
    return $this->biz->getToken($request->all());
}

//刷新token
public function refreshToken(Request $request)
{
    return $this->biz->refreshToken($request->header('key'));
}

⑦、结果

请求生成token: http://xx.com/api/auth/token?device_id=15616513

结果:

{
    "error": 0,
    "msg": "ok",
    "data": {
        "key": "eyJpdiI6InloN1NNc3R5aVdzWE9WZjdoYzFFXC9nPT0iLCJ2YWx1ZSI6ImxSTE1YOXJ0bllDMERseEFiaWc0a1E9PSIsIm1hYyI6IjM5ZjAzM2U0OWNhNzY1NjRkNzY1N2RjZmQwYmNlMDYwNWMzZjM0MDJkNTRlODg3OWI3NGE5MmY2MzA2YmFjYTQifQ==",
        "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NjQ3OTgyNjksIm5iZiI6MTU2NDc5ODI2OSwiZXhwIjoxNTY0ODA1NDY5fQ.T8e4eCBfU6AeZ5RpRLPGTgd5MaY5asyglfV7s-1A57M",
        "expire": 7200
    }
}

请求刷新token:

{
    "error": 0,
    "msg": "ok",
    "data": {
        "key": "eyJpdiI6Inl1enBMXC9EMkpJVGROTWdMdGI1UUZRPT0iLCJ2YWx1ZSI6IkozMEhJbWMydlNtR3h6dkNQVnJKaUE9PSIsIm1hYyI6ImI3MWYyZjUxY2I5OTEyZjQyZjFjODQwNTlkMDAwYjA0YmEzYWY3YmRlNzdjOGU4ZGVhZmM0YTNkZmE3MDEwYWMifQ==",
        "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NjQ3OTg2NjMsIm5iZiI6MTU2NDc5ODY2MywiZXhwIjoxNTY0ODA1ODYzfQ.c1mlhyP8SmfaCHFx7d8CeaL1SjdGVaXUXDkBEuKn3uo",
        "expire": 7200
    }
}

其它定义了app.sign中间件中的路由请求接口时都需要携带key和token

posted @ 2019-08-03 10:22  pine007  阅读(1373)  评论(0编辑  收藏  举报