JWT 在项目中的实际使用
14

关于JWT 可以参考 JWT 完整使用详解 这里说一下在实际项目中的使用:

laravel 5.5
php7.1
"tymon/jwt-auth": "1.*@rc"

JWT token 的刷新 和 基于 JWT 实现单用户登陆

TOKEN 的刷新

理解

用户登陆后 获取到 tokenA (这个时候 tokenA 的有效期是60分钟)

通过 tokenA 获取到 tokenB (这个时候 tokenB 的有效期是60分钟,tokenA 开始进行 60秒的 倒计时,60秒后就会被拉黑)

tokenB 换取 tokenC (成功后 tokenB 开始进行60秒倒计时,60秒后会被拉黑)

env 配置如下:

JWT_SECRET=jbSn01PbHsFoRzEqHtuOsM3rV3FCsGcI
JWT_BLACKLIST_ENABLED=true  # 是否开启toekn黑名单 生产环境需要开启 宽限时间需要开启黑名单(默认是开启的),黑名单保证过期token不可再用,最好打开
JWT_BLACKLIST_GRACE_PERIOD=60 # 设定宽限时间,单位:秒
JWT_REFRESH_TTL=20160 # 刷新时间  单位:分钟
JWT_TTL=60 # 有效时间  单位:分钟

单用户登陆

基于 JWT token 的单用户登陆, 在 token 的载荷配置中做一点手脚即可:

<?php

namespace App\Http\Controllers;

use Auth;
use App\Business\UserBusiness;
use App\Transformers\UserTransformer;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Facades\JWTAuth;
use Tymon\JWTAuth\Facades\JWTFactory;

/**
 * 用户相关
 *
 * @Resource("user", uri="/api")
 */
class UserController extends Controller
{

    protected $userBusiness;

    /**
     * UserController constructor.
     * @param UserBusiness $userBusiness
     */
    public function __construct(UserBusiness $userBusiness)
    {
        $this->userBusiness = $userBusiness;
    }

    /**
     * 用户登陆
     *
     * 使用 `username` 和 `password` 进行登陆。
     *
     * @Post("/login")
     * @Versions({"v1"})
     * @Transaction({
     *      @Request({"username": "foo", "password": "bar"}),
     *      @Response(200, body={"code":1,"time":"2018-08-10 09:32:44","message":"success","data":{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9hcGkuc21hcnR2aWRlby5jb21cL2FwaVwvbG9naW4iLCJpYXQiOjE1MzM4NjQ3NjQsImV4cCI6MTUzMzg2ODM2NCwibmJmIjoxNTMzODY0NzY0LCJqdGkiOiJsZTJObzRLVDZlT0NyVnZCIiwic3ViIjoiZGVkZjYyZTI5MDA0MTFlODgzM2I1NGVlNzVlNTM1MzciLCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.OdJlE_pUuttqIxsjKF-FAcZOhMYitS69fh18lPZAYmQ","token_type":"bearer","expires_in":3600}}),
     *      @Response(200, body=
     *          {
     *              "message": "用户不存在",
     *              "code": 4000,
     *              "status_code": 500
     *          }
     *      )
     *
     * })
     */
    public function login(Request $request)
    {
        $rules = [
            'username' => 'required',
            'password' => 'required'
        ];

        $this->_validate($request, $rules);

        $spbill_create_ip = $request->header('x-real-ip')?: $request->ip();

        iLog('----------spbill_create_ip------------'. $spbill_create_ip);

        $username = $request->username;
        $password = $request->password;

        $login_time = time();
        $user = $this->userBusiness->dologin($username,$password,$spbill_create_ip,$login_time);

        // Get the token
        $factory = JWTFactory::customClaims([
            'sub'   => $user->guid,
            'ip' => $spbill_create_ip,
            'login_time' => $login_time
        ]);
        $payload = $factory->make();
        $token = JWTAuth::encode($payload);

        return $this->_response($this->respondWithToken((string)$token));

    }

    protected function respondWithToken($token)
    {
        return [
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => JWTAuth::factory()->getTTL() * 60
        ];
    }
}
<?php

namespace App\Http\Middleware;

use App\Business\ResponseException;
use App\Common\ResponseCode;
use Closure;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;

class ClientCheck extends BaseMiddleware
{
    /**
     * @param $request
     * @param Closure $next
     * @return mixed
     * @throws ResponseException
     */
    public function handle($request, Closure $next)
    {
        $array = $this->auth->payload()->jsonSerialize();

        $user = $this->auth->user();

        if (key_exists('ip',$array) && key_exists('login_time',$array)) {
            if ($array['ip'] != $user->ip || $array['login_time'] != $user->login_at) throw new ResponseException("该账户已在其他设备登陆",ResponseCode::OTHER_CLIENT_LOGIN);
        }
        return $next($request);
    }
}
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 11

这是什么注释语法

/**
     * 用户登陆
     *
     * 使用 `username` 和 `password` 进行登陆。
     *
     * @Post("/login")
     * @Versions({"v1"})
     * @Transaction({
     *      @Request({"username": "foo", "password": "bar"}),
     *      @Response(200, body={"code":1,"time":"2018-08-10 09:32:44","message":"success","data":{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9hcGkuc21hcnR2aWRlby5jb21cL2FwaVwvbG9naW4iLCJpYXQiOjE1MzM4NjQ3NjQsImV4cCI6MTUzMzg2ODM2NCwibmJmIjoxNTMzODY0NzY0LCJqdGkiOiJsZTJObzRLVDZlT0NyVnZCIiwic3ViIjoiZGVkZjYyZTI5MDA0MTFlODgzM2I1NGVlNzVlNTM1MzciLCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.OdJlE_pUuttqIxsjKF-FAcZOhMYitS69fh18lPZAYmQ","token_type":"bearer","expires_in":3600}}),
     *      @Response(200, body=
     *          {
     *              "message": "用户不存在",
     *              "code": 4000,
     *              "status_code": 500
     *          }
     *      )
     *
     * })
     */
3周前

@lovecn 这是一种高级的注释语法

2周前
dividez

@lovecn "dingo/api": "2.0.0-alpha1",

2周前

请教关于刷新token, 返回前端处理方法。

在中间件判断token过期后,此时在不影响本次请求情况下,怎么刷新token 返回前端比较好。项目中用到了。不知道如何处理是好?

  • 代码如下
       public function handle($request, Closure $next){
            try {
                    if ($user = $this->auth->parseToken()->authenticate()) {
                        return $next($request);
                    }
                return response()->json(['code'=>402,'data'=>[],'msg'=>'未登录']);
            }catch (\Tymon\JWTAuth\Exceptions\TokenInvalidException $exception) {
                //token 放到黑名单列表了
                return response()->json(['code' => 490,'data'=>[],'msg'=>$exception->getMessage()]);
            }catch (\Tymon\JWTAuth\Exceptions\TokenExpiredException $exception) {
                //token已过期 但是 没放入黑名单前。去刷新token 并返回去。
                try {
                    // 刷新用户的 token
                    $token = $this->auth->refresh();
                    Log::info('过期后的刷新--'.$token);
                    // 使用一次性登录以保证此次请求的成功
                    Auth::guard('api')->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
                    auth()->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
                } catch (JWTException $exception) {
                    // 如果捕获到此异常,即代表 refresh 也过期了,用户无法刷新令牌,需要重新登录。
                    return response()->json(['code'=>407,'data'=>[],'msg'=>$exception->getMessage()]);
                }
            }
            // 在响应头中返回新的 token   这里处理的不太好 
            return $this->setAuthenticationHeader($next($request), $token);
    }
1周前

我上面的那个写法,已经解决了刷新token了、 只是postman 用的不是很熟。没看到新返回的token.

1周前
dividez
6天前

是的。就是这样的。谢谢。@dividez

6天前
dividez

file

file

甚至可以这样用

6天前

求解,你的刷新具体的实现

5天前
dividez

使用 jwt 的 中间件

file

5天前

@dividez 用jwt.renew是每次请求都对token 进行了刷新,请问题如何只是在过期的时候才进行刷新,否则就不刷新呢

4天前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!