您的位置:

Python JWT -- 使用JSON Web令牌保障安全的身份验证

一、什么是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)功能和良好的可扩展性。