JSON Web Tokens(JWT)是一种广泛使用的开放标准(RFC 7519),用于在网络应用环境间传递声明(claim)。它定义了一种紧凑且自包含的方式,用于在各方之间传递安全信息。
JWT的好处包括:
描述: JWT作为一种轻量级的数据格式,可以在不同的编程语言和平台上无缝工作。
代码示例: 假设我们使用Python的pyjwt
库来生成和解析JWT。
import jwt
import datetime
# 密钥
SECRET_KEY = 'YOUR_SECRET_KEY'
# 用户信息
payload = {
'user_id': 123456,
'username': 'johndoe',
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=30) # 过期时间
}
# 生成JWT
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
print('Generated Token:', token)
描述: 服务端不需要存储用户的会话信息,每个JWT都包含了所需的所有信息。
代码示例: 使用Node.js和jsonwebtoken
库来生成无状态的JWT。
const jwt = require('jsonwebtoken');
// 密钥
const secretKey = 'YOUR_SECRET_KEY';
// 用户信息
const payload = {
userId: 1,
userName: 'John Doe'
};
// 设置过期时间
const options = {
expiresIn: '2h' // 2小时后过期
};
// 生成JWT
const token = jwt.sign(payload, secretKey, options);
console.log('Generated Token:', token);
描述: JWT通过使用密钥进行签名,确保了数据的完整性和来源的验证。
代码示例: 使用Java的jjwt
库来验证JWT的签名。
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class JwtExample {
public static void main(String[] args) {
String secretKey = "YOUR_SECRET_KEY";
String token = Jwts.builder()
.setSubject("John Doe")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
System.out.println("Generated Token: " + token);
// 验证JWT
boolean isTokenValid = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody()
.getSubject()
.equals("John Doe");
System.out.println("Is Token Valid: " + isTokenValid);
}
}
描述: JWT允许开发者添加自定义的声明(claims),以扩展其功能。
代码示例: 在Node.js中添加自定义声明。
const jwt = require('jsonwebtoken');
// 密钥
const secretKey = 'YOUR_SECRET_KEY';
// 用户信息和自定义声明
const payload = {
userId: 1,
userName: 'John Doe',
customClaim: 'custom_value' // 自定义声明
};
// 生成JWT
const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });
console.log('Generated Token with Custom Claim:', token);
描述: JWT的载荷可以自定义,包含额外的用户信息或应用特定的数据。
代码示例: 使用Python添加额外的用户信息。
import jwt
# 密钥
SECRET_KEY = 'YOUR_SECRET_KEY'
# 用户信息和额外数据
payload = {
'user_id': 123456,
'username': 'johndoe',
'extra_data': {
'role': 'admin',
'department': 'IT'
}
}
# 生成JWT
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
print('Generated Token with Extra Data:', token)
以下是一些防止JWT TOKEN被篡改的策略:
描述: HTTPS通过SSL/TLS加密传输数据,保护数据不被窃听或篡改。
代码示例: 使用Python的http.server
和ssl
模块创建HTTPS服务器。
from http.server import HTTPServer, BaseHTTPRequestHandler
import ssl
# 自定义请求处理器
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
# 发送HTTP状态码200
self.send_response(200)
self.end_headers()
# 返回内容
self.wfile.write(b'Hello, HTTPS world!')
# 创建HTTP服务器对象,监听localhost的443端口
httpd = HTTPServer(('localhost', 443), SimpleHTTPRequestHandler)
# 使用ssl模块包装socket以支持HTTPS
httpd.socket = ssl.wrap_socket(
httpd.socket,
keyfile='path/to/your/private.key', # 私钥文件路径
certfile='path/to/your/certificate.crt', # 证书文件路径
server_side=True # 表示服务器端
)
# 启动服务器
print("Starting HTTPS server on https://localhost:443")
httpd.serve_forever()
描述: 使用强密钥对JWT进行签名,确保JWT的安全性。
代码示例: 使用Python的pyjwt
库生成签名的JWT。
import jwt
import datetime
# 定义密钥
SECRET_KEY = 'YOUR_SECRET_KEY'
# 定义JWT的负载内容
payload = {
'user_id': 123456,
'username': 'johndoe',
# 设置过期时间为当前时间后1小时
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}
# 使用HS256算法对payload进行编码(生成JWT)
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
print('Signed JWT:', token)
描述: 定期更换密钥并安全存储,避免泄露。
代码示例: 使用环境变量来存储密钥。
import os
import jwt
# 从环境变量中获取密钥
SECRET_KEY = os.getenv('JWT_SECRET_KEY')
# 使用SECRET_KEY生成和验证JWT的代码与前面类似
# ...
描述: 选择更强的算法,如RSA或ECDSA。
代码示例: 使用pyjwt
库和RSA算法生成JWT。
from jwt import JWT, PyJWK, PyJWKSet
# 假设你已经有了一个RSA私钥和公钥
private_key = """-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----"""
public_key = """-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----"""
# 从公钥创建JWKSet对象
jwk_set = PyJWKSet.from_pem(public_key)
jwk = jwk_set.keys[0] # 获取公钥对象
# 定义JWT的负载内容
payload = {'user_id': 123456, 'username': 'johndoe'}
# 使用RS256算法对payload进行编码(生成JWT)
token = JWT().encode(payload, private_key, algorithm='RS256')
print('JWT with RS256:', token)
描述: 确保客户端和服务器端都验证JWT的签名算法。
代码示例: 使用pyjwt
库验证JWT的签名算法。
# 假设token是使用RS256算法签名的
try:
# 解码JWT,验证签名
decoded_token = jwt.decode(token, key=jwk, algorithms=["RS256"])
print('JWT Algorithm Valid:', True)
except jwt.PyJWTError:
print('JWT Algorithm Valid:', False)
描述: 确保只有授权的应用和服务可以访问和验证JWT。
代码示例: 使用Flask框架设置JWT访问控制。
from flask import Flask, request, jsonify
from functools import wraps
import jwt
app = Flask(__name__)
# 定义密钥
SECRET_KEY = 'YOUR_SECRET_KEY'
# 创建装饰器,用于保护需要token验证的路由
def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
token = request.args.get('token') # 从请求中获取token
if not token:
return jsonify({'message': 'Token is missing!'}), 403
# 验证token
try:
data = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
except:
return jsonify({'message': 'Token is invalid!'}), 403
return f(*args, **kwargs)
return decorated
# 创建一个需要token验证的路由
@app.route('/protected')
@token_required
def protected():
return jsonify({'message': 'This is a protected endpoint'})
if __name__ == '__main__':
# 运行Flask应用,使用adhoc创建自签名证书
app.run(ssl_context='adhoc')
单点登录(SSO)是一种允许用户使用单一凭证在多个相关但独立的系统间访问的机制。JWT可以有效地实现SSO,以下是其实现过程:
描述: 用户首次登录时,系统验证身份并生成JWT。
代码示例:
import jwt
import datetime
# 用户登录验证函数
def authenticate_user(username, password):
# 这里应有验证逻辑,例如查询数据库
# 假设用户名和密码正确
return True
# 生成JWT的函数
def generate_jwt(user_id):
try:
payload = {
'user_id': user_id,
'exp': datetime.datetime.utcnow() + datetime.timedelta(days=1) # JWT有效期1天
}
# 使用HS256算法和密钥生成JWT
jwt_token = jwt.encode(payload, 'YOUR_SECRET_KEY', algorithm='HS256')
return jwt_token
except Exception as e:
print("Error generating JWT:", e)
return None
描述: 将JWT存储在用户的浏览器中,通常通过HTTP Only Cookie。
代码示例: 使用Flask设置HTTP Only Cookie。
from flask import Flask, request, make_response
app = Flask(__name__)
@app.route('/login')
def login():
username = request.args.get('username')
password = request.args.get('password')
if authenticate_user(username, password):
user_id = 1 # 假设的用户ID
jwt_token = generate_jwt(user_id)
resp = make_response(jwt_token) # 创建响应对象
resp.set_cookie('jwt_token', jwt_token, httponly=True) # 设置HTTP Only Cookie
return resp
else:
return 'Authentication failed', 401
if __name__ == '__main__':
app.run()
描述: 用户携带JWT访问其他系统,服务端验证JWT有效性。
代码示例: 使用Flask验证JWT。
# 装饰器,用于保护需要JWT验证的路由
def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
token = request.cookies.get('jwt_token') # 从Cookie中获取JWT
if not token:
return 'Token is missing', 403
try:
# 解码JWT,验证有效性
payload = jwt.decode(token, 'YOUR_SECRET_KEY', algorithms=['HS256'])
except jwt.ExpiredSignatureError:
return 'Token has expired', 403
except jwt.InvalidTokenError:
return 'Invalid token', 403
return f(*args, **kwargs)
return decorated
@app.route('/protected')
@token_required
def protected_resource():
return 'You have accessed a protected resource!'
描述: JWT设置过期时间,并提供刷新机制。
代码示例: 使用Flask实现刷新令牌。
# 刷新令牌的函数
def refresh_token():
# 假设从数据库或会话中获取用户信息
user_id = 1 # 假设的用户ID
return generate_jwt(user_id)
@app.route('/refresh')
def refresh():
token = request.args.get('token')
if token:
try:
# 验证传入的token是否有效
payload = jwt.decode(token, 'YOUR_SECRET_KEY', algorithms=['HS256'])
return refresh_token() # 返回新的JWT
except jwt.InvalidTokenError:
return 'Invalid token', 403
return 'No token provided', 400
在单点登录系统中,当用户在新设备上登录时,可能需要将之前的设备上的会话挤掉,以确保安全性。以下是一些实现策略:
详细策略: 建立一个中心化的会话存储,可以是一个数据库或分布式缓存系统,用于跟踪每个用户的活跃会话及其设备标识。每当用户登录时,系统检查该用户的现有会话并根据需要更新或创建新会话。
代码示例:
# 假设使用Redis作为会话存储
import redis
r = redis.Redis()
def create_or_update_session(user_id, device_id, session_token):
# 存储会话,key为用户ID和设备ID的组合
r.set(f"session:{user_id}:{device_id}", session_token, ex=3600) # 设置1小时过期
def get_session(user_id, device_id):
# 根据用户ID和设备ID获取会话
return r.get(f"session:{user_id}:{device_id}")
def invalidate_all_sessions_except(user_id, current_device_id, session_token):
# 遍历用户所有会话,除了当前设备外,其他全部使无效
current_session = f"session:{user_id}:{current_device_id}"
sessions = r.scan(match=f"session:{user_id}:*", count=100) # 获取所有会话
for session_key in sessions[1]:
if session_key != current_session:
r.delete(session_key) # 删除旧会话
详细策略: 为每个用户会话生成一个唯一的刷新令牌,存储在安全的地方(如服务器端数据库)。当用户从新设备登录时,使旧设备的刷新令牌失效。
代码示例:
def refresh_session(user_id, new_refresh_token):
# 更新用户的刷新令牌
r.set(f"refresh_token:{user_id}", new_refresh_token, ex=7200) # 设置2小时过期
def revoke_old_refresh_token(user_id, new_refresh_token):
# 使旧刷新令牌无效
old_refresh_token = r.get(f"refresh_token:{user_id}")
if old_refresh_token and old_refresh_token != new_refresh_token:
r.delete(f"refresh_token:{user_id}") # 删除旧刷新令牌
详细策略: 实现一个黑名单系统,用于存储被撤销的令牌。在验证JWT时,首先检查令牌是否在黑名单中。
代码示例:
def add_to_blacklist(jwt_token):
# 将JWT添加到黑名单
r.sadd("blacklist", jwt_token)
def check_token_in_blacklist(jwt_token):
# 检查JWT是否在黑名单中
return r.sismember("blacklist", jwt_token)
详细策略: 当检测到用户从新设备登录时,要求用户完成多因素认证或发送一次性密码到用户的已验证邮箱或手机。
代码示例:
def force_reauthentication(user_id):
# 发送一次性密码到用户的邮箱或手机
# 这里只是一个示例,实际实现需要集成邮件或短信服务
send_otp_to_user(user_id) # 假设这是发送OTP的函数
详细策略: 当用户的会话被挤掉时,通过电子邮件、短信或应用内通知等方式,及时通知用户。
代码示例:
def notify_user(user_id, message):
# 发送通知给用户
# 这里只是一个示例,实际实现需要集成邮件或短信服务
send_email_to_user(user_id, message) # 假设这是发送邮件的函数
在您的文章最后,关于JWK(JSON Web Key)和JWKS(JSON Web Key Set)的补充可以这样写:
除了JWT本身,JWK和JWKS也是在处理JWT时经常使用的概念,它们为JWT的安全性和灵活性提供了额外的支持。
JWK(JSON Web Key) 是一种JSON数据结构,用于表示公钥或私钥。JWK的格式允许在网络应用间安全地传输和存储密钥信息,而不需要直接暴露密钥的原始格式。
JWKS(JSON Web Key Set) 是一个JWK的集合,通常用于存储多个密钥,并且可以动态地添加、更新或删除密钥。JWKS常用于需要使用多个密钥进行签名或验证的场景,例如在多租户应用中。
以下是使用Python pyjwt
库和 cryptography
库操作JWK和JWKS的示例:
from jwt import jwk_from_pem
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives import serialization
# 从PEM格式的私钥文件加载私钥
with open('private.pem', 'rb') as key_file:
private_key = load_pem_private_key(
key_file.read(),
password=None,
backend=serialization.NoBackend()
)
# 将私钥转换为JWK
jwk = jwk_from_pem(private_key)
# 打印JWK信息
print('JWK:', jwk)
# 假设有多个JWK,可以创建JWKS
jwks = {
'keys': [jwk] # JWKS包含一个或多个JWK
}
# 打印JWKS信息
print('JWKS:', jwks)
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。