Laravel 集成 JWT 认证

JWT github地址:https://github.com/tymondesigns/jwt-auth

安装

执行以下命令安装最新稳定版本:

composer require tymon/jwt-auth

或者添加如下信息到你的 composer.json 文件中 并执行 composer update:

"tymon/jwt-auth": "0.5.*"

然后注册服务提供者到 Laravel config/app.php 中的 providers 数组:

Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,

添加 JWT 门面:

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

然后发布相应配置文件:

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

为 JWT token 生成密钥:

php artisan jwt:generate

添加 JWT 中间件:

'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class,
'jwt.refresh' => \Tymon\JWTAuth\Middleware\RefreshToken::class,

PS:jwt.auth 中间件验证 token 必要性、合法性、过期时间 jwt.refresh 解析请求中 token,并顺序刷新 token(同时废弃老的 token )并将其作为下一个响应的一部分

在 config/jwt.php 中,你可以配置以下选项:

  • ttl:token有效期(分钟)
  • refresh_ttl:刷新token时间(分钟)
  • algo:token签名算法
  • user:指向User模型的命名空间路径
  • identifier:用于从token的sub中获取用户
  • require_claims:必须出现在token的payload中的选项,否则会抛出TokenInvalidException异常
  • blacklist_enabled:如果该选项被设置为false,那么我们将不能废止token,即使我们刷新了token,前一个token仍然有效
  • providers:完成各种任务的具体实现,如果需要的话你可以重写他们
  • User —— providers.user:基于sub获取用户的实现
  • JWT —— providers.jwt:加密/解密token
  • Authentication —— providers.auth:通过证书/ID获取认证用户
  • Storage —— providers.storage:存储token直到它们失效

创建Token

 public function authenticate(Request $request)
{
    // grab credentials from the request
    $credentials = $request->only('email', 'password');

    try {
        // attempt to verify the credentials and create a token for the user
        if (! $token = JWTAuth::attempt($credentials)) {
            return response()->json(['error' => 'invalid_credentials'], 401);
        }
    } catch (JWTException $e) {
        // something went wrong whilst attempting to encode the token
        return response()->json(['error' => 'could_not_create_token'], 500);
    }

    // all good so return the token
    return response()->json(compact('token'));
}

还可以直接通过用户对象实例创建token:

$user = User::first();
$token = JWTAuth::fromUser($user);

自定义token

使用 JWTFactory 门面可以轻易的创建自定义token

public function authenticate(Request $request)
{
    // grab credentials from the request
    $credentials = $request->only('email', 'password');

    try {
        // attempt to verify the credentials and create a token for the user
        if (!JWTAuth::attempt($credentials)) {
            return response()->json(['error' => 'invalid_credentials'], 401);
        }
    } catch (JWTException $e) {
        // something went wrong whilst attempting to encode the token
        return response()->json(['error' => 'could_not_create_token'], 500);
    }

    $customClaims = ['user_id' => auth()->user()->id, 'project_id' => '16'];
    $payload = JWTFactory::make($customClaims);

    $token = JWTAuth::encode($payload)->get();

    // all good so return the token
    return response()->json(compact('token'));
}

用户认证

http发送一个需要认证通过的请求,需要设置Authorization头:

Authorization: Bearer {yourtokenhere}

或者在 url 后面加上 token 参数:

http://api.mysite.com/me?token={yourtokenhere}

认证用户信息

我们从 token 中获取认证用户只需要调用 JWTAuth::parseToken()->authenticate()

public function getAuthenticatedUser()
{
    try {

        if (! $user = JWTAuth::parseToken()->authenticate()) {
            return response()->json(['user_not_found'], 404);
        }

    } catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {

        return response()->json(['token_expired'], $e->getStatusCode());

    } catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {

        return response()->json(['token_invalid'], $e->getStatusCode());

    } catch (Tymon\JWTAuth\Exceptions\JWTException $e) {

        return response()->json(['token_absent'], $e->getStatusCode());

    }

    // the token is valid and we have found the user via the sub claim
    return response()->json(compact('user'));
}

解析自定义token

我们要解析上面自定义 token,不能使用 JWTAuth::parseToken()->authenticate() 这个方法。这种方法是获取认证用户的信息,并不是我们上面自定义的信息,要想获取自定义信息,需要用到 JWTAuth::parseToken()->getPayload() 方法。

public function getAuthenticatedUser()
{
    try {

        if (! $user = JWTAuth::parseToken()->getPayload()->get()) {
            return response()->json(['user_not_found'], 404);
        }

    } catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {

        return response()->json(['token_expired'], $e->getStatusCode());

    } catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {

        return response()->json(['token_invalid'], $e->getStatusCode());

    } catch (Tymon\JWTAuth\Exceptions\JWTException $e) {

        return response()->json(['token_absent'], $e->getStatusCode());

    }
    // the token is valid and we have found the user via the sub claim
    return response()->json(compact('user'));
}

注意:$token = JWTAuth::encode($payload)->get(); 和 ! $user = JWTAuth::parseToken()->getPayload()->get() 中都调用了 get() 方法,如果不调用,response()->json() 会转换不成功,永远返回空的 josn 数据。

end