一、什么是JSON Web令牌?
JSON Web令牌(JSON Web Token,JWT)是一种基于JSON的安全传输信息的开放标准,主要用于在网络应用中传递声明,以便于在服务端进行校验,保证数据的安全性和完整性。
一个JWT实际上就是一个字符串,它由三部分组成,分别是头部(header)、负载(payload)和签名(signature)。其中,头部和负载都是JSON格式的数据,在JWT中用Base64Url编码表示。签名则是用于验证消息的完整性的一串字符,由头部、负载、密钥和加密算法共同生成。以下是一个JWT的示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ. SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
上面的JWT由三部分组成,分别是以.分隔的头部、负载和签名。下面我们将对其进行逐一解析。
1. JWT的头部
JWT的头部主要用于描述关于该JWT的最基本信息,如所使用的加密算法等。头部的格式如下:
{ "alg": "HS256", "typ": "JWT" }
其中,alg表示签名算法,常用的有HS256、HS384、HS512、RS256、RS384、RS512、ES256、ES384、ES512等。
2. JWT的负载
JWT的负载主要用于存储需要传递的数据,如用户ID、过期时间、权限等信息。负载的格式是一个JSON对象,可以自由地定义自己需要的字段。
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
上面示例中,sub表示用户ID,name表示用户名,iat表示JWT的签发时间。
3. JWT的签名
JWT的签名是用于验证JWT的完整性,防止数据被篡改。签名的生成需要使用一个密钥,对头部和负载进行加密,然后再与密钥一起通过指定的签名算法生成签名。签名的生成示例代码如下:
import base64 import hmac import hashlib header = base64.urlsafe_b64encode(b'{"alg": "HS256", "typ": "JWT"}').rstrip(b'=') payload = base64.urlsafe_b64encode(b'{"sub": "1234567890", "name": "John Doe", "iat": 1516239022}').rstrip(b'=') secret = 'your-256-bit-secret' message = header + b'.' + payload signature = hmac.new(key=secret.encode('utf-8'), msg=message, digestmod=hashlib.sha256).digest() jwt = message + b'.' + base64.urlsafe_b64encode(signature).rstrip(b'=') print(jwt.decode('utf-8'))
以上代码主要实现了对于header和payload的base64编码以及签名的生成。其中,b'='是多余的填充字符,使用rstrip函数进行去除,生成最终的jwt。
二、Python中如何使用JWT实现身份验证?
在使用JWT进行身份验证时,常见的使用方式是把JWT编码后存放在请求的头部,如下所示:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
需要注意的是,Bearer是一种认证机制,用于告知服务器该请求是带有访问令牌的请求。
在Python中,我们可以借助PyJWT库来实现JWT的生成和验证功能。以下是一个使用PyJWT实现JWT身份验证的示例:
import jwt import datetime from functools import wraps from flask import Flask, jsonify, request app = Flask(__name__) app.config['SECRET_KEY'] = 'your-secret-key' def token_required(func): @wraps(func) def decorated(*args, **kwargs): token = request.headers.get('Authorization').split(' ')[1] if not token: return jsonify({'message' : 'Token is missing!'}), 401 try: data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"]) except: return jsonify({'message' : 'Token is invalid!'}), 401 return func(*args, **kwargs) return decorated @app.route('/login') def login(): auth = request.authorization if auth and auth.password == 'password': token = jwt.encode({'user' : auth.username, 'exp' : datetime.datetime.utcnow() + datetime.timedelta(minutes=30)}, app.config['SECRET_KEY'], algorithm='HS256') return jsonify({'token' : token}) return make_response('Could not verify!', 401, {'WWW-Authenticate' : 'Basic realm="Login Required"'}) @app.route('/protected') @token_required def protected(): return jsonify({'message' : 'Logged in successfully!'}) if __name__ == '__main__': app.run()
上面的代码中,我们使用了一个token_required装饰器,用于验证请求是否带有正确的JWT令牌。在登录时,首先需要进行身份验证,接着生成JWT令牌并返回。在需要进行身份验证的接口中,使用token_required装饰器进行验证,对未授权的请求进行拦截。
三、使用JWT带来的好处
1. 无需存储用户状态
使用JWT进行用户身份验证时,无需在服务器端存储用户的登录状态,从而减轻服务器的负担,提高服务器的性能。
2. 单点登录(SSO)功能
使用JWT进行身份验证时,可以将JWT令牌存储在客户端的Cookie或LocalStorage中,实现跨域单点登录(SSO)的功能。
3. 良好的可扩展性
使用JWT进行身份验证时,负载中可以存储任意的字段信息,可以方便地扩展业务功能,不需要额外修改现有的代码。
四、小结
通过以上介绍,我们了解了JSON Web令牌(JWT)是一种基于JSON的安全传输信息的开放标准,可以在网络应用中实现用户身份验证的功能。在Python中,我们可以通过PyJWT库来实现JWT的生成和验证。使用JWT带来的好处包括无需存储用户状态、单点登录(SSO)功能和良好的可扩展性。