1. JWT概述
JSON Web Token通常简称为JWT,它是一种开放标准(RFC 7519)。随着RESTful架构的流行,越来越多的项目使用JWT作为用户身份认证的方式。JWT相当于是三个JSON对象经过编码后,用.
分隔并组合到一起,这三个JSON对象分别是头部(header)、载荷(payload)和签名(signature),如下图所示。
2. 头部
{
"alg": "HS256",
"typ": "JWT"
}
其中,alg
属性表示签名的算法,默认是HMAC SHA256(简写成HS256
);typ
属性表示这个令牌的类型,JWT中都统一书写为JWT
。
3. 载荷
载荷部分用来存放实际需要传递的数据。JWT官方文档中规定了7个可选的字段:
- iss :签发人
- exp:过期时间
- sub:主题
- aud:受众
- nbf:生效时间
- iat:签发时间
- jti:编号
除了官方定义的字典,我们可以根据应用的需要添加自定义的字段,如下所示。
{
"sub": "1234567890",
"nickname": "jackfrued",
"role": "admin"
}
4. 签名
签名部分是对前面两部分生成一个指纹,防止数据伪造和篡改。实现签名首先需要指定一个密钥。这个密钥只有服务器才知道,不能泄露给用户。然后,使用头部指定的签名算法(默认是HS256
),按照下面的公式产生签名。
HS256(base64Encode(header) + '.' + base64Encode(payload), secret)
- 算出签名以后,把头部、载荷、签名三个部分拼接成一个字符串,每个部分用
.
进行分隔,这样一个JWT就生成好了。
5. JWT的优缺点
使用JWT的优点非常明显,包括:
- 更容易实现水平扩展,因为令牌保存在浏览器中,服务器不需要做状态管理。
- 更容易防范CSRF攻击,因为在请求头中添加
localStorage
或sessionStorage
中的token必须靠JavaScript代码完成,而不是自动添加到请求头中的。 - 可以防伪造和篡改,因为JWT有签名,伪造和篡改的令牌无法通过签名验证,会被认定是无效的令牌。
当然,任何技术不可能只有优点没有缺点,JWT也有诸多缺点,大家需要在使用的时候引起注意,具体包括:
- 可能会遭受到XSS攻击(跨站脚本攻击),通过注入恶意脚本执行JavaScript代码获取到用户令牌。
- 在令牌过期之前,无法作废已经颁发的令牌,要解决这个问题,还需要额外的中间层和代码来辅助。
- JWT是用户的身份令牌,一旦泄露,任何人都可以获得该用户的所有权限。为了降低令牌被盗用后产生的风险,JWT的有效期应该设置得比较短。对于一些比较重要的权限,使用时应通过其他方式再次对用户进行认证,例如短信验证码等。
6. 使用PyJWT生成和验证令牌
在Python代码中,可以使用三方库PyJWT
生成和验证JWT,下面是安装PyJWT
的命令。
pip install pyjwt
生成令牌。
payload = {
'exp': datetime.datetime.utcnow() + datetime.timedelta(days=1),
'userid': 10001
}
token = jwt.encode(payload, settings.SECRET_KEY).decode()
验证令牌。
try:
token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTQ4NzIzOTEsInVzZXJpZCI6MTAwMDF9.FM-bNxemWLqQQBIsRVvc4gq71y42I9m2zt5nlFxNHUo'
payload = jwt.decode(token, settings.SECRET_KEY)
except InvalidTokenError:
raise AuthenticationFailed('无效的令牌或令牌已经过期')