侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

JWT源码实现逻辑详解

Laravel  /  管理员 发布于 2年前   834

描述:

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准((RFC 7519). 该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。


详细信息可查看什么是 JWT – JSON WEB TOKEN(https://www.jianshu.com/p/576dbf44b2ae),这里就不做过多介绍。

在项目中我使用了 tymon/jwt-auth 扩展包,所以根据对这个包进行源码分析,了解其具体的实现逻辑

通过集成这个包,我们可以在 config.auth.php 中修改看守器的驱动,将 driver 修改为 jwt

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

在用户登录,可通过 Auth::guard('api')->attempt(['email'=>$'email,'password'=>$password]); 校验用户登录并返回 token


其中校验密码的过程,我们一般将用户的密码通过 bcrypt () 函数进行加密,通过 bcrypt () 函数即使密码相同,生成的字符串也不相同。然后通过 password_verify () 函数验证密码是否和散列值匹配。


用户登录校验完成后,会返回如下的字符串,这个字符串就是 token, 它分为三个部分,第一部分我们称它为头部(header), 第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature),通过. 将字符串连接在一起。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9lbGVtZW50LXNob3AudGVzdFwvYWRtaW5cL2FwaVwvbG9naW4iLCJpYXQiOjE2MTE4OTAwNDUsImV4cCI6MTYxMjEwNjA0NSwibmJmIjoxNjExODkwMDQ1LCJqdGkiOiJMTnQ3N01yaXlNUDVKRmFaIiwic3ViIjoyLCJwcnYiOiJhMjNiNTczZGM3M2E0MDdlOGRlNTNiNDg2ZjM2ODg2YWRmNzBjNDgzIn0.B4bHALzb5lg0z-G2iU3wwiYb4r18-wUa0TVH_V1X1IE


通过查看源码实现,其中 encode () 用于生成 token,decode () 用于解析 token

/**
     * Create a JSON Web Token.
     *
     * @param  array  $payload
     *
     * @throws \Tymon\JWTAuth\Exceptions\JWTException
     *
     * @return string
     */
    public function encode(array $payload)
    {
        // Remove the signature on the builder instance first.
        $this->builder->unsign();
        try {
            foreach ($payload as $key => $value) {
                $this->builder->set($key, $value);
            }
            $this->builder->sign($this->signer, $this->getSigningKey());
        } catch (Exception $e) {
            throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e);
        }
        return (string) $this->builder->getToken();
    }

继续查看生成 token 逻辑

/**
     * Returns the resultant token
     *
     * @return Token
     */
    public function getToken(Signer $signer = null, Key $key = null)
    {
        //为声明的加密算法,这里为HS256,可在./config/jwt.php中配置algo参数修改
        $signer = $signer ?: $this->signer;
        //token加密私钥,只存储在服务端,也是实现token最重要的一环,可在./config/jwt.php中配置secret参数修改
        //一般通过php artisan jwt:secret命令生成
        $key = $key ?: $this->key;
        if ($signer instanceof Signer) {
            //在token头部添加加密算法
            $signer->modifyHeader($this->headers);
        }
        //生成token的第一部分和第二部分
        $payload = [
            //将["typ" => "JWT","alg" => "HS256"]数组转为字符串并进行base64加密形成token的header
            $this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->headers)),
            //其中的$this->claims为token的载荷数组转为字符串并进行base64加密形成token的payload,内容包括
            //iss: jwt签发者
            //sub: jwt所面向的用户 
            //exp: jwt的过期时间,这个过期时间必须要大于签发时间
            //nbf: 定义在什么时间之前,该jwt都是不可用的.
            //iat: jwt的签发时间
            //jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
            $this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->claims))
        ];
        //生成签名,使用hash_hmac($this->getAlgorithm(), $payload, $key->getContent(), true)生成签名,其中$this->getAlgorithm()为加密算法HS256,$payload为token的第一,二部分,$key为加密私钥
        $signature = $this->createSignature($payload, $signer, $key);
        if ($signature !== null) {
            //将签名进行base64加密返回
            $payload[] = $this->encoder->base64UrlEncode($signature);
        }
        return new Token($this->headers, $this->claims, $signature, $payload);
    }

查看解析 token 的代码

/**
     * Decode a JSON Web Token.
     *
     * @param  string  $token
     *
     * @throws \Tymon\JWTAuth\Exceptions\JWTException
     *
     * @return array
     */
    public function decode($token)
    {
        try {
            //解析token,将token分割为三部分头部,负载,签名。对数据进行base64解码并json_decode转为数组,若解析失败抛出异常
            $jwt = $this->parser->parse($token);
        } catch (Exception $e) {
            throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e);
        }
        //使用hash_equals内置函数对token解析的签名与hash_hmac($this->getAlgorithm(), $payload, $key->getContent(), true)比较是否相同,若相同则表示认证通过,不同则直接抛出异常
        if (! $jwt->verify($this->signer, $this->getVerificationKey())) {
            throw new TokenInvalidException('Token Signature could not be verified.');
        }
        //认证通过返回payload的内容
        return (new Collection($jwt->getClaims()))->map(function ($claim) {
            return is_object($claim) ? $claim->getValue() : $claim;
        })->toArray();
    }


总结:

1.以上就是生成和解析 token 的大致逻辑,其中加密的关键还是在于服务器生成的私钥,若私钥流失,客户端就可以自己根据逻辑生成 token。

2.payload 要防止存放敏感信息,因为该部分是客户端可解密的部分。

3.想自己造轮子也知道逻辑。

4.面试的时候问到也不虚了。


转:https://learnku.com/articles/54009



  • 上一条:
    Laravel内核分析-设计模式之观察者模式
    下一条:
    论不要在mysql中使用[utf8]编码,如果要用请用[utf8mb4]
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在Laravel项目中使用中间件方式统计用户在线时长功能代码示例(0个评论)
    • 在Laravel中构建业务流程模型(0个评论)
    • 在Laravel项目中的实现无密码认证之:发送邮箱链接授权(0个评论)
    • Laravel 10.4版本发布(0个评论)
    • 在laravel框架中的5个HTTP客户端技巧分享(0个评论)
    • 近期文章
    • 如何优雅处理async await错误推荐:await-to-js库(0个评论)
    • lodash工具库(0个评论)
    • 在Laravel项目中使用中间件方式统计用户在线时长功能代码示例(0个评论)
    • 在Laravel中构建业务流程模型(0个评论)
    • windows系统中安装FFMpeg及在phpstudy环境php7.3 + php-ffmpeg扩展的使用流程步骤(0个评论)
    • 在go语言中对浮点的数组、切片(slice)进行正向排序和反向排序(0个评论)
    • 在go语言中对整数数组、切片(slice)进行排序和反向排序(0个评论)
    • 在go语言中对字符串数组、切片(slice)进行排序和反向排序(0个评论)
    • 最新国内免注册ChatGPT体验站_ChatGPT镜像站访问链接地址2023/3/28持续更新(0个评论)
    • 在Laravel项目中的实现无密码认证之:发送邮箱链接授权(0个评论)
    • 近期评论
    • 博主 在

      2023年国务院办公厅春节放假通知:1月21日起休7天中评论 @ xiaoB 你只管努力,剩下的叫给天意;天若有情天亦老,..
    • xiaoB 在

      2023年国务院办公厅春节放假通知:1月21日起休7天中评论 会不会春节放假后又阳一次?..
    • BUG4 在

      你翻墙过吗?国内使用vpn翻墙可能会被网警抓,你需了解的事中评论 不是吧?..
    • 博主 在

      go语言+beego框架中获取get,post请求的所有参数中评论 @ t1  直接在router.go文件中配就ok..
    • Jade 在

      如何在MySQL查询中获得当月记录中评论 Dear zongscan.com team, We can skyroc..
    • 2016-10
    • 2016-11
    • 2017-07
    • 2017-08
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-08
    • 2020-09
    • 2020-10
    • 2020-11
    • 2021-01
    • 2021-02
    • 2021-03
    • 2021-04
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-03
    • 2022-04
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客