jwt生成token及校验

263 阅读3分钟

1、token介绍

  • token:服务端生成的一串字符串,可以解决频繁登录的问题;
  • 它作为客户端进行请求的一个令牌: 第一次登录后,服务器生成一个token返回给客户端;
    客户端只需要带上token来请求数据即可,无需再次带上用户名和密码;
  • 目的:可以减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮;

2、jwt生成、校验token原理

2.1 生成原理

jwt的生成token格式如下,即:由 . 连接的三段字符串组成。
生成规则如下:

  • 第一段HEADER部分,固定包含算法和token类型,对此json进行base64url加密,这就是token的第一段;
headers = {
    'typ': 'jwt',
    'alg': 'HS256'
}
  • 第二段PAYLOAD部分,包含一些数据,对此json进行base64url加密,这就是token的第二段;
{
    "name""wenjin",
    "id": "1",
    …
}
  • 第三段SIGNATURE部分,把前两段的base密文通过.拼接起来,然后对其进行HS256加密,再然后对hs256密文进行base64url加密,最终得到token的第三段。
base64url(
    HMACSHA256(
      base64UrlEncode(header) + "." + base64UrlEncode(payload),
      your-256-bit-secret (秘钥加盐)
    )
)

最后将三段字符串通过 .拼接起来就生成了jwt的token。
注意:base64url加密是先做base64加密,然后再将 - 替代 + 及 _ 替代 / 。

2.2 校验原理

一般在认证成功后,把jwt生成的token返回给用户,以后用户再次访问时候需要携带token,此时jwt需要对token进行超时合法性校验。

  • 将token分割成 header_segmentpayload_segmentcrypto_segment 三部分

  • 对第一部分header_segment进行base64url解密,得到header

  • 对第二部分payload_segment进行base64url解密,得到payload

  • 对第三部分crypto_segment进行base64url解密,得到signature

  • 对第三部分signature部分数据进行合法性校验

    • 拼接前两段密文,即:signing_input
    • 从第一段明文中获取加密算法,默认:HS256
    • 使用 算法+盐 对signing_input 进行加密,将得到的结果和signature密文进行比较。

image.png

3、安全相关

  • jwt生成的token第一段、第二段内容采用base64编码,可以反向被解码;
  • 但是第三段内容在加密的时候,加了盐的原因【用户无法获取盐的信息】,因此jwt生成的token不会被破解;
    例如:用户发送请求时,可以修改第二段用户信息内容,但是因为不知道盐的信息,无法生成正确的第三段内容

4、相关代码

4.1 安装第三方包pyjwt

pip install pyjwt

4.2 代码

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import jwt
import datetime
from jwt import exceptions
from django.conf import settings


def create_token(payload, timeout=20):
    """
    :param payload:  例如:{'user_id':1,'username':'wupeiqi'}用户信息
    :param timeout: token的过期时间,默认20分钟
    :return:
    """
    headers = {
        'typ': 'jwt',
        'alg': 'HS256'
    }
    payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(seconds=timeout)
    result = jwt.encode(payload=payload, key=settings.SECRET_KEY.encode('utf-8'), algorithm="HS256", headers=headers)
    return result


def parse_payload(token):
    """
    对token进行和发行校验并获取payload
    :param token:
    :return:
    """
    try:
        verified_payload = jwt.decode(token, settings.SECRET_KEY.encode('utf-8'), algorithms=["HS256"])
        return True, verified_payload
    except exceptions.ExpiredSignatureError:
        error = 'token已失效'
    except jwt.DecodeError:
        error = 'token认证失败'
    except jwt.InvalidTokenError:
        error = '非法的token'
    return False, error