1. JWT概述

JSON Web Token通常简称为JWT,它是一种开放标准(RFC 7519)。随着RESTful架构的流行,越来越多的项目使用JWT作为用户身份认证的方式。JWT相当于是三个JSON对象经过编码后,用.分隔并组合到一起,这三个JSON对象分别是头部(header)、载荷(payload)和签名(signature),如下图所示。
json-web-token.png

2. 头部

  1. {
  2. "alg": "HS256",
  3. "typ": "JWT"
  4. }

其中,alg属性表示签名的算法,默认是HMAC SHA256(简写成HS256);typ属性表示这个令牌的类型,JWT中都统一书写为JWT

3. 载荷

载荷部分用来存放实际需要传递的数据。JWT官方文档中规定了7个可选的字段:

  • iss :签发人
  • exp:过期时间
  • sub:主题
  • aud:受众
  • nbf:生效时间
  • iat:签发时间
  • jti:编号

除了官方定义的字典,我们可以根据应用的需要添加自定义的字段,如下所示。

  1. {
  2. "sub": "1234567890",
  3. "nickname": "jackfrued",
  4. "role": "admin"
  5. }

4. 签名

签名部分是对前面两部分生成一个指纹,防止数据伪造和篡改。实现签名首先需要指定一个密钥。这个密钥只有服务器才知道,不能泄露给用户。然后,使用头部指定的签名算法(默认是HS256),按照下面的公式产生签名。

  1. HS256(base64Encode(header) + '.' + base64Encode(payload), secret)
  1. 算出签名以后,把头部、载荷、签名三个部分拼接成一个字符串,每个部分用.进行分隔,这样一个JWT就生成好了。

5. JWT的优缺点

使用JWT的优点非常明显,包括:

  1. 更容易实现水平扩展,因为令牌保存在浏览器中,服务器不需要做状态管理。
  2. 更容易防范CSRF攻击,因为在请求头中添加localStoragesessionStorage中的token必须靠JavaScript代码完成,而不是自动添加到请求头中的。
  3. 可以防伪造和篡改,因为JWT有签名,伪造和篡改的令牌无法通过签名验证,会被认定是无效的令牌。

当然,任何技术不可能只有优点没有缺点,JWT也有诸多缺点,大家需要在使用的时候引起注意,具体包括:

  1. 可能会遭受到XSS攻击(跨站脚本攻击),通过注入恶意脚本执行JavaScript代码获取到用户令牌。
  2. 在令牌过期之前,无法作废已经颁发的令牌,要解决这个问题,还需要额外的中间层和代码来辅助。
  3. JWT是用户的身份令牌,一旦泄露,任何人都可以获得该用户的所有权限。为了降低令牌被盗用后产生的风险,JWT的有效期应该设置得比较短。对于一些比较重要的权限,使用时应通过其他方式再次对用户进行认证,例如短信验证码等。

6. 使用PyJWT生成和验证令牌

在Python代码中,可以使用三方库PyJWT生成和验证JWT,下面是安装PyJWT的命令。

  1. pip install pyjwt

生成令牌。

  1. payload = {
  2. 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=1),
  3. 'userid': 10001
  4. }
  5. token = jwt.encode(payload, settings.SECRET_KEY).decode()

验证令牌。

  1. try:
  2. token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTQ4NzIzOTEsInVzZXJpZCI6MTAwMDF9.FM-bNxemWLqQQBIsRVvc4gq71y42I9m2zt5nlFxNHUo'
  3. payload = jwt.decode(token, settings.SECRET_KEY)
  4. except InvalidTokenError:
  5. raise AuthenticationFailed('无效的令牌或令牌已经过期')