JWT令牌
# JWT令牌
JSON Web Token——无状态认证标准。生成/验证/刷新 Token,API 认证、单点登录(SSO)、OAuth 2.0。
# 一、JWT 结构解析
JWT = Header.Payload.Signature
Base64 Base64 HMAC/RSA 签名
示例 Token(三个点分隔):
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbGljZSJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
|_______header______|_____payload_____|______________signature_______________|
1
2
3
4
5
6
2
3
4
5
6
# 二、Python — PyJWT
pip install pyjwt
# 如需 RS256:pip install pyjwt[crypto]
1
2
2
# 2.1 生成 Token(签名)
#!/usr/bin/env python3
"""JWT 生成——编码 + 签名"""
import jwt, time
SECRET = "my-secret-key-for-hs256"
# ---- 生成 Token ----
payload = {
"sub": "user_12345", # subject——用户标识
"name": "Alice",
"role": "admin",
"iat": int(time.time()), # issued at——签发时间
"exp": int(time.time()) + 3600 # 过期时间(1 小时后)
}
token = jwt.encode(payload, SECRET, algorithm="HS256")
print(f"Token:\n{token}")
# 如果是字符串类型——直接用(PyJWT 2.x 返回字符串)
# 如果是 bytes——.decode()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 2.2 验证与解析 Token
#!/usr/bin/env python3
import jwt, time
SECRET = "my-secret-key-for-hs256"
token = "..." # 上面生成的 token
try:
# 验证 + 解码——一步完成
decoded = jwt.decode(
token,
SECRET,
algorithms=["HS256"],
options={"require": ["exp", "sub"]} # 要求必须有 exp 和 sub
)
print(f"✅ Token 有效")
print(f" 用户: {decoded['sub']}")
print(f" 角色: {decoded['role']}")
print(f" 过期: {time.ctime(decoded['exp'])}")
except jwt.ExpiredSignatureError:
print("❌ Token 已过期")
except jwt.InvalidTokenError as e:
print(f"❌ Token 无效: {e}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 2.3 RS256 签名(非对称,推荐生产用)
#!/usr/bin/env python3
"""JWT RS256——用 RSA 签名,服务只需公钥即可验证"""
import jwt, time
from cryptography.hazmat.primitives import serialization
# ---- 生成 Token(持有私钥的认证服务)----
with open('private.pem', 'rb') as f:
private_key = f.read()
payload = {"sub": "user_12345", "role": "admin",
"iat": int(time.time()), "exp": int(time.time()) + 3600}
token = jwt.encode(payload, private_key, algorithm="RS256")
print(f"RS256 Token: {token[:50]}...")
# ---- 验证 Token(任何服务只需公钥)----
with open('public.pem', 'rb') as f:
public_key = f.read()
try:
decoded = jwt.decode(token, public_key, algorithms=["RS256"])
print(f"✅ 验证通过: {decoded['sub']}")
except jwt.InvalidTokenError:
print("❌ 验证失败")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 2.4 不验证签名的情况下解码(调试用)
#!/usr/bin/env python3
"""仅解码 Header 和 Payload——不验证签名,只看内容"""
import jwt
token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NSJ9.xxx"
# 不验证——直接解码(调试/检查过期时间用)
decoded = jwt.decode(token, options={"verify_signature": False})
print(decoded) # {'sub': '12345'}
# 或者只读 Header
header = jwt.get_unverified_header(token)
print(header) # {'alg': 'HS256', 'typ': 'JWT'}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 三、Shell 解码 JWT
#!/bin/bash
# ===== 一行解码 JWT Payload(不验证签名)=====
jwt_decode() {
local token="$1"
# 取 Payload 部分(第二个 . 段)
local payload_b64=$(echo "$token" | cut -d. -f2)
# Base64 解码(处理 URL 安全 Base64 和可能的填充缺失)
echo "$payload_b64" \
| tr '_-' '/+' \
| awk '{print $0 "=="}' \
| base64 -d 2>/dev/null \
| python3 -m json.tool 2>/dev/null || echo "解码失败"
}
# 使用
jwt_decode "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NSJ9.xxx"
# ===== 检查 Token 过期时间 =====
check_jwt_expiry() {
local token="$1"
local exp=$(echo "$token" | cut -d. -f2 \
| tr '_-' '/+' \
| base64 -d 2>/dev/null \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('exp',0))")
local now=$(date +%s)
if [[ "$exp" -gt "$now" ]]; then
echo "✅ Token 有效,剩余 $(( (exp - now) / 60 )) 分钟"
else
echo "❌ Token 已过期"
fi
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 四、实战:JWT 认证中间件
#!/usr/bin/env python3
"""
Flask / FastAPI 风格 JWT 认证装饰器——可直接集成到 Web 应用
"""
import jwt, time, functools
from flask import request, jsonify
SECRET = "your-secret-key"
def create_token(user_id: str, role: str = "user", expire_minutes: int = 60) -> str:
"""生成 JWT"""
return jwt.encode({
"sub": user_id,
"role": role,
"iat": int(time.time()),
"exp": int(time.time()) + expire_minutes * 60
}, SECRET, algorithm="HS256")
def refresh_token(token: str, extend_minutes: int = 60) -> str:
"""刷新 Token——重新生成但保留原用户信息"""
try:
old = jwt.decode(token, SECRET, algorithms=["HS256"],
options={"verify_exp": False}) # 允许过期 Token
return create_token(old["sub"], old.get("role", "user"), extend_minutes)
except jwt.InvalidTokenError:
raise ValueError("无效的 Token,无法刷新")
def login_required(f):
"""装饰器——自动从 Authorization Header 提取并验证 JWT"""
@functools.wraps(f)
def wrapper(*args, **kwargs):
auth = request.headers.get("Authorization", "")
if not auth.startswith("Bearer "):
return jsonify({"error": "Missing token"}), 401
token = auth[7:]
try:
payload = jwt.decode(token, SECRET, algorithms=["HS256"])
request.current_user = payload # 注入用户信息
return f(*args, **kwargs)
except jwt.ExpiredSignatureError:
return jsonify({"error": "Token expired"}), 401
except jwt.InvalidTokenError:
return jsonify({"error": "Invalid token"}), 401
return wrapper
# ---- 使用示例 ----
# @app.route("/api/me")
# @login_required
# def me():
# return jsonify({"user": request.current_user["sub"]})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
⚠️ 安全提醒:密钥绝不能硬编码!用环境变量
os.getenv("JWT_SECRET")或 Vault。
上次更新: 2026/06/17, 12:47:39