Python 生成 JWT(json web token) 及 解析方式
<p><span style="font-size: 16px">一.关于 jwt 的原理及概念可以自行在网络上搜索了解一下,这里推荐一篇写的比较好的博客</span></p><p><span style="font-size: 16px"> <span style="color: rgba(255, 0, 0, 1)"><span style="color: rgba(255, 0, 0, 1)">深入了解Json Web Token之概念篇</span></span></span></p>
<p> </p>
<p><span style="font-size: 16px"> 另附 JWT 的官方文档: <span style="color: rgba(255, 0, 0, 1)"><span style="color: rgba(255, 0, 0, 1)">https://jwt.io/introduction/</span></span><br></span></p>
<p> </p>
<p><span style="font-size: 16px">二.python 对于 jwt 的实现, 目前已经存在了一些第三方的库, 相信学习过 python 的程序猿都知道 itsdangerous 这个库了, 它的底层原理就是基于 jwt 进行实现的</span></p>
<p><span style="font-size: 16px"> <span style="color: rgba(255, 0, 0, 1)">这里需要进行提醒的是:</span> </span></p>
<p><span style="font-size: 16px"> itsdangerous (使用固定密钥/字符串进行加密, jwt 有多种加密方式, 这只是其中一种, 建议先去了解一下)所生成的 token 仍然是可以被破译从而看到 jwt 的 payload(有效负载) 里的数据, 只不过因为破译者并不知道加密的密钥, 也就无法对数据进行篡改, 所以如果是私密的数据, 就不应该使用 jwt 进行传递, 如 账号的密码, 以防止泄露. 如果需要传递私密数据, 解决办法是,对 payload 的数据进行加密,从而杜绝非法破译者看到 payload 内的任何信息,但是目前加密payload的操作不是很普及,在不加密 payload 的前提下, jwt 比较适合进行非受信任端的身份验证, 此时即使接收方破译了 token, 看到了 payload 的数据, 也不会造成太大的影响, 因为数据是无法被篡改的(当接收方将 token 值返回给服务器后, 需要使用相同的密钥进行解密, 所以服务器的密钥一定要保管好), 只要接收方将 token 原封不动的返回给服务器, 那么服务器就可以根据 token 值的内容来确认接收方身份的合法性,而不需要关心接收方是否看到过 payload 的内容. </span></p>
<p> <span style="color: rgba(255, 0, 0, 1)">简而言之, 除非额外对 payload 加密过, 否则就不要在 jwt 中传递不可被第三方获知的私密数据 </span></p>
<p><span style="font-size: 16px">三. python实现生成 json web token</span></p>
<p><span style="font-size: 16px"> 环境: python3.6.7<br></span></p>
<p><span style="font-size: 16px"> 依赖包: jwt, time<br></span></p>
<p><span style="font-size: 16px"> 1)JWT </span>的签名算法有三种。</p>
<p><span style="font-size: 16px"> <span style="font-size: 15px">1.</span></span><span style="font-size: 15px">对称加密HMAC【哈希消息验证码】 HS256/HS384/HS512 </span></p>
<p><span style="font-size: 15px"> 这种加密方式没有公钥,私钥之分, 也就是只有一个密钥, 这种加密方式适用于: 服务器将生成的jwt发送给接收方, 接收方将其返回给服务器, 服务器解析 jwt, 完成身份验证.<br></span></p>
<p><span style="font-size: 15px"> 2.非对称加密RSASSA【RSA签名算法】RS256/RS384/RS512</span></p>
<p><span style="font-size: 15px"> 3.ECDSA【椭圆曲线数据签名算法】 ES256/ES384/ES512</span></p>
<p><span style="font-size: 15px"> <span style="font-size: 16px">2).对称加密HMAC 生成 jwt</span><br></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> time
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> jwt
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> payload</span>
token_dict =<span style="color: rgba(0, 0, 0, 1)"> {</span>
<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">iat</span><span style="color: rgba(128, 0, 0, 1)">'</span>: time.time(),<span style="color: rgba(0, 128, 0, 1)"># </span><span style="color: rgba(0, 128, 0, 1)">时间戳<br> 'name': 'lowman'# 自定义的参数<br></span>}</pre>
<pre><span style="color: rgba(94, 100, 139, 1); font-style: italic">"""payload <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">中一些固定参数名称的意义<span style="color: rgba(94, 100, 139, 1); font-style: italic">, <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">同时可以在<span style="color: rgba(94, 100, 139, 1); font-style: italic">payload<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">中自定义参数"""</span></span></span></span></span></span><br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># iss<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">【<span style="color: rgba(94, 100, 139, 1); font-style: italic">issuer<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">】发布者的<span style="color: rgba(94, 100, 139, 1); font-style: italic">url<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">地址<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># sub <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">【<span style="color: rgba(94, 100, 139, 1); font-style: italic">subject<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">】该<span style="color: rgba(94, 100, 139, 1); font-style: italic">JWT<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">所面向的用户,用于处理特定应用,不是常用的字段<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># aud <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">【<span style="color: rgba(94, 100, 139, 1); font-style: italic">audience<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">】接受者的<span style="color: rgba(94, 100, 139, 1); font-style: italic">url<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">地址<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># exp <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">【<span style="color: rgba(94, 100, 139, 1); font-style: italic">expiration<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">】 该<span style="color: rgba(94, 100, 139, 1); font-style: italic">jwt<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">销毁的时间;<span style="color: rgba(94, 100, 139, 1); font-style: italic">unix<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">时间戳<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># nbf<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">【<span style="color: rgba(94, 100, 139, 1); font-style: italic">not before<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">】 该<span style="color: rgba(94, 100, 139, 1); font-style: italic">jwt<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">的使用时间不能早于该时间;<span style="color: rgba(94, 100, 139, 1); font-style: italic">unix<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">时间戳<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># iat <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">【<span style="color: rgba(94, 100, 139, 1); font-style: italic">issued at<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">】 该<span style="color: rgba(94, 100, 139, 1); font-style: italic">jwt<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">的发布时间;<span style="color: rgba(94, 100, 139, 1); font-style: italic">unix <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">时间戳<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># jti <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">【<span style="color: rgba(94, 100, 139, 1); font-style: italic">JWT ID<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">】 该<span style="color: rgba(94, 100, 139, 1); font-style: italic">jwt<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">的唯一<span style="color: rgba(94, 100, 139, 1); font-style: italic">ID<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">编号</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span><span style="color: rgba(0, 0, 0, 1)"><br>
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> headers</span>
headers =<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">alg</span><span style="color: rgba(128, 0, 0, 1)">'</span>: <span style="color: rgba(128, 0, 0, 1)">"HS</span><span style="color: rgba(128, 0, 0, 1)">256</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 声明所使用的算法</span><span style="color: rgba(0, 128, 0, 1)"><br>}<br><br></span><span style="color: rgba(94, 100, 139, 1); font-style: italic">"""headers 中一些固定参数名称的意义"""<br># jku: <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">发送<span style="color: rgba(94, 100, 139, 1); font-style: italic">JWK<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">的地址;最好用<span style="color: rgba(94, 100, 139, 1); font-style: italic">HTTPS<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">来传输<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># jwk: <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">就是之前说的<span style="color: rgba(94, 100, 139, 1); font-style: italic">JWK<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># kid: jwk<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">的<span style="color: rgba(94, 100, 139, 1); font-style: italic">ID<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">编号<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># x5u: <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">指向一组<span style="color: rgba(94, 100, 139, 1); font-style: italic">X509<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">公共证书的<span style="color: rgba(94, 100, 139, 1); font-style: italic">URL<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># x5c: X509<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">证书链<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># x5t<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">:<span style="color: rgba(94, 100, 139, 1); font-style: italic">X509<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">证书的<span style="color: rgba(94, 100, 139, 1); font-style: italic">SHA-1<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">指纹<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># x5t#S256: X509<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">证书的<span style="color: rgba(94, 100, 139, 1); font-style: italic">SHA-256<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">指纹<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># typ: <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">在原本未加密的<span style="color: rgba(94, 100, 139, 1); font-style: italic">JWT<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">的基础上增加了<span style="color: rgba(94, 100, 139, 1); font-style: italic"> JOSE <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">和<span style="color: rgba(94, 100, 139, 1); font-style: italic"> JOSE+ JSON<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">。<span style="color: rgba(94, 100, 139, 1); font-style: italic">JOSE<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">序列化后文会说及。适用于<span style="color: rgba(94, 100, 139, 1); font-style: italic">JOSE<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">标头的对象与此<span style="color: rgba(94, 100, 139, 1); font-style: italic">JWT<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">混合的情况。<br><span style="color: rgba(94, 100, 139, 1); font-style: italic"># crit: <span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">字符串数组,包含声明的名称,用作实现定义的扩展,必须由<span style="color: rgba(94, 100, 139, 1); font-style: italic"> this->JWT<span style="color: rgba(94, 100, 139, 1); font-style: italic; font-family: "AR PL UKai CN"">的解析器处理。不常见。</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span><span style="color: rgba(0, 0, 0, 1)"><br>
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 调用jwt库,生成json web token</span>
jwt_token = jwt.encode(token_dict,# payload, 有效载体
<span style="color: rgba(128, 0, 0, 1)">"zhananbudanchou1234678</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,# 进行加密签名的密钥
algorithm</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">HS256</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 指明签名算法方式, 默认也是HS256</span>
headers=headers<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> json web token 数据结构包含两部分, payload(有效载体), headers(标头)</span>
).decode(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">ascii</span><span style="color: rgba(128, 0, 0, 1)">'</span>)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> python3 编码后得到 bytes, 再进行解码(指明解码的格式), 得到一个str</span>
<span style="color: rgba(0, 0, 255, 1)">print</span>(jwt_token)<br><br># 个人测试生成结果如下: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6Ijk1MjcifQ.eyJpYXQiOjE1NTkyNzY5NDEuNDIwODgzNywibmFtZSI6Imxvd21hbiJ9.GyQhOJK8FKD_Gd-ggSEDPPP1Avmz3M5NDVnmfOfrEIY</pre>
</div>
<p> <span style="font-size: 16px">3) 使用 python 对 jwt 进行解析</span></p>
<div class="cnblogs_code">
<pre></pre>
<pre><span style="color: rgba(0, 0, 255, 1)">import<span style="color: rgba(0, 0, 0, 1)"> jwt</span></span></pre>
<pre><span style="color: rgba(0, 128, 0, 1)"><br>#</span><span style="color: rgba(0, 128, 0, 1)"> 将上面生成的 jwt 进行解析认证</span>
<span style="color: rgba(0, 0, 0, 1)">
jwt_token </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6Ijk1MjcifQ.eyJpYXQiOjE1NTkyNzY5NDEuNDIwODgzNywibmFtZSI6Imxvd21hbiJ9.GyQhOJK8FKD_Gd-ggSEDPPP1Avmz3M5NDVnmfOfrEIY"</span><span style="color: rgba(0, 0, 0, 1)">
data </span>=<span style="color: rgba(0, 0, 0, 1)"> None
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 需要解析的 jwt 密钥 使用和加密时相同的算法</span>
data = jwt.decode(token, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">zhananbudanchou1234678</span><span style="color: rgba(128, 0, 0, 1)">"</span>, algorithms=[<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">HS256</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">])
</span><span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> Exception as e:
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 如果 jwt 被篡改过; 或者算法不正确; 如果设置有效时间, 过了有效期; 或者密钥不相同; 都会抛出相应的异常</span>
<span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(e)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 解析出来的就是 payload 内的数据</span>
<span style="color: rgba(0, 0, 255, 1)">print</span>(data)<br><br># 输出: {'iat': 1559276941.4208837, 'name': 'lowman'}</pre>
</div>
<p> <span style="font-size: 16px"> </span></p>
<p><span style="font-size: 16px"> 4)如果是使用私钥公钥进行加密解密的方式(由请求方使用私钥进行加密生成 jwt, 接收方使用公钥解密), 只需要将相应参数更换成私钥(将私钥证书读取出来, 赋值给相应参数即可), 并使用双方约定好的的签名算法</span></p>
<p> </p>
<p><span style="font-size: 16px"> 5) python 对于 jwt 的实现, 已经有了 <span style="font-size: 16px">itsdangerous</span> 这个库做了很好的支撑, 使用起来还是很方便的, 大家可以自行去了解一下.<br></span></p>
<p><span style="font-size: 16px"> <span style="font-size: 16px"><span style="font-size: 16px">itsdangerous 官方文档: <span style="color: rgba(255, 0, 0, 1)"><span style="color: rgba(255, 0, 0, 1)">https://itsdangerous.readthedocs.io/en/1.1.x/</span></span><br></span></span></span></p>
<p> </p>
</div>
<div id="MySignature" role="contentinfo">
当我仰望星空, 看见了涛涛江水, 闻到了人声鼎沸;可当我蓦然回望,再也触摸不到那逝去的时光,再也看不到那夕阳下的少年!<br><br>
来源:https://www.cnblogs.com/lowmanisbusy/p/10930856.html
頁:
[1]