文章目录
今日内容
- JWT、JWS、JWE
- 短信验证实现
- JWT实现方案
- 请求钩子实现jwt验证身份
1. JWT、JWS、JWE
1.1 JWT与JWS
JWT介绍
-
什么是JWT
JWT即Json Web Token的简写。是为了在网络应用环境间传递声明的一种基于JSON的开放标准。 -
JWT用途
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便从服务端获取资源。 -
JWT应用场景
该Token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。
注意
, JWT并不等于JWS,JWS(JSON WEB SIGNATURE)只是JWT的一种实现,除了JWS外,JWE(JSON Web Encryption)也是JWT的一种实现。
JWS详解: JSON Web Signature
JWS有三部分组成: Header + Payload + Signature, 实例如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWS生成过程:
-
Header头部: JWT的基本信息。 一个json数据{“alg”:“HS256”,“typ”:“JWT”},经 base64 编码过后的字符串,alg代表加密算法为"哈希256",typ类型为"JWT"。
-
Payload载荷: 用于存放自定义信息。一个json数据经base64编码得到的字符串。如 {“uid”: 1, “username”: “jack”, exp: “过期时间”}
-
Signature签名: 由header编码字符串与payload编码字符串用"."连接后,通过header中声明的加密算法,使用密钥secret(盐)进行一次加密,生成的加密数据再进行base64编码,最后生成签名字符串。整个过程中"盐"是非常重要的, 千万不能泄露。
JWT验证原理
- 服务端获取请求中的token值,使用"."将token值分为三段。
- 将第一段与第二段使用base64进行解码, 第一段可以拿到签名的加密算法, 第二段可以拿到用户信息和过期时间, 判断token是否过期
- 将第一段和第二段用"."连接起来, 用第一段中的加密算法进行加密,然后base64编码,最后与第三段进行对比, 如果一致, 则token合法,否则token值不合法。
1.2 pyjwt的安装与使用
安装:
# 安装
pip install pyjwt
核心方法:
import jwt
# 核心方法
token = jwt.encode(payload, secret, algorithm='HS256')
- token: 生成的token的值, 有三段组成.
- encode: 传入payload载荷, secret盐, algorithm加密算法
payload = jwt.decode(token, secret, algorithms=["HS256"])
pyjwt使用
创建一个test_jwt_project
- 创建 jwt token 生成与校验的工具模块 —jwt_utli.py
- 登录成功,服务端签发token,前端重定向index首页
- 登录失败,提示失败信息
- 直接访问index首页时, 验证token,验证通过返回index首页, 否则重定向到login页面。
import jwt
from flask import current_app
# jwt_util.py
# 生成jwt token
def generate_jwt_token(payload, secret=None, expire=5):
"""
payload, dict, 用户的载荷信息
secret, str, 加盐信息
expire, datatime/int, 过期时间
return token 字符串
"""
# 类型检查逻辑
...
# 过期时间
exp = datetime.utcnow() + timedelta(days=expire) if isinstance(expire, int) else expire
_payload = {
"exp": exp
}
# 合并载荷信息
_payload.update(payload)
# 未传入secret时,使用默认的值
if not secret:
secret = current_app.config.get("JWT_SECRET")
if secret is None:
secret = "laufing666"
# 编码,生成token
token = jwt.encode(_payload, secret, algorithm="HS256")
return token
# 验证jwt token
def validate_jwt_token(token, secret=None):
"""
token, 字符串, 待解码的token
secret, 编码时的加盐信息
return tuple, 验证信息
"""
# 未传入secret, 使用默认值
if not secret:
secret = current_app.config.get("JWT_SECRET")
if not secret:
secret = "laufing666"
# 解码 token
try:
payload = jwt.decode(token, secret, algorithms=["HS256"])
# 解码错误
except DecodeError:
return None, "非法token"
# token 过期
except ExpiredSignatureError:
return None, "token已过期"
# 验证成功
return payload, "验证成功"
# 配置文件 settings.py
import pymysql
pymysql.install_as_MySQLdb()
# 配置数据库
SQLALCHEMY_DATABASE_URI = "mysql://lauf:lauf123@localhost:3306/flask_0702"
SQLALCHEMY_TRACK_MODIFICATION = Tru