在调用第三方API时,签名是保证请求身份真实性与数据完整性的关键步骤。服务端通过验证签名来判断请求是否合法、是否被篡改。本文将基于Python语言,从原理到代码,覆盖主流签名方式,并重点解析一种基于HMAC-SHA256的签名实现(类似WPS4风格),同时给出OAuth 2.0、JWT、RSA非对称签名和MD5参数签名的实战示例。
1. 签名调用通用流程
无论采用何种算法,签名调用通常按以下步骤进行:
- 准备请求参数:包括业务参数(如文件ID、金额)、公共参数(如access_key、timestamp、nonce)。
- 按照API文档规定的规则(如排序、拼接、添加密钥)生成待签名字符串,并使用指定加密算法(如HMAC-SHA256、RSA-SHA256)计算签名。
- 将签名放入请求头(Header)或请求体中发送。
- 服务端收到请求后,用相同规则和密钥重新计算签名,比对一致则通过验证,否则返回4xx错误。
2. 基于HMAC-SHA256的请求签名实现(核心示例)
以下代码演示了一个完整的签名生成与HTTP请求发起过程。该实现源自类似WPS4的API签名规范,通用性好,可直接套用于其他要求HMAC-SHA256签名的接口。- import json
- import requests
- import hashlib
- import time
- import hmac
- access_key = 'xxxxxxxxxxxxxxx'
- secret_key = 'xxxxxxxxxxxxxxx'
- def _wps4_sig(method, url, date, body):
- print(body)
- if body is None:
- bodySha = ""
- else:
- bodySha = hashlib.sha256(body.encode('utf-8')).hexdigest()
- content = "xxx-4" + method + url + "application/json" + date + bodySha
- print(content)
- signature = hmac.new(secret_key.encode('utf-8'), content.encode('utf-8'), hashlib.sha256).hexdigest()
- return "xxx-4 %s:%s" % (access_key, signature)
- def wps4_request(method, host, uri, body=None, cookie=None, headers=None):
- requests.packages.urllib3.disable_warnings()
- if body is not None and not isinstance(body, str):
- body = json.dumps(body)
- date = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
- header = {"Content-type": "application/json"}
- header['xxx-Docs-Date'] = date
- header['xxx-Docs-Authorization'] = _wps4_sig(method, uri, date, body)
- if headers is not None:
- for key, value in headers.items():
- header[key] = value
- url = "%s%s" % (host, uri)
- r = requests.request(method, url, data=body, headers=header, cookies=cookie, verify=False)
- return r.status_code, r.text
- def edit(file_id=1):
- uri = '/api/edit/v1/files/{0}/link?type=w'.format(file_id)
- result = wps4_request('GET', 'http://xx.xx.xx.xxx:xxxx/open', uri)
- print(result[0])
- print(result[1])
- if __name__ == '__main__':
- edit()
复制代码 关键点解释:
- _wps4_sig 函数将请求方法、URI、Content-Type、日期和请求体SHA256摘要拼接成字符串,再用HMAC-SHA256算法以secret_key为密钥计算签名。返回格式为“xxx-4 access_key:signature”。
- 日期必须按照RFC 1123格式生成,服务端会验证该时间戳,防止重放攻击。
- 请求体为空时 bodySha 置为空字符串,否则对body进行SHA256哈希。
- wps4_request 自动处理日期、签名头和请求发送,返回状态码和响应文本。
- 该签名方式适用于需要高安全级别的RESTful API调用,如办公套件、云存储等服务的接口。
3. 其他常见签名方式及Python实现
3.1 OAuth 2.0 客户端凭证模式
适用于服务器到服务器的授权(如Google、GitHub API)。- import requests
- def get_oauth_token(client_id, client_secret, token_url):
- data = {
- 'grant_type': 'client_credentials',
- 'client_id': client_id,
- 'client_secret': client_secret,
- }
- resp = requests.post(token_url, data=data)
- return resp.json()['access_token']
- # 获取 token 并调用
- client_id = 'your_client_id'
- client_secret = 'your_secret'
- token_url = 'https://auth.example.com/token'
- token = get_oauth_token(client_id, client_secret, token_url)
- headers = {'Authorization': f'Bearer {token}'}
- resp = requests.get('https://api.example.com/data', headers=headers)
复制代码 注意:access_token有过期时间,需缓存并定期刷新。
3.2 JWT(JSON Web Token)
常用于用户登录态和微服务间认证。JWT自包含用户信息,无需服务端存储会话。- import jwt
- import time
- SECRET_KEY = 'your_secret_key'
- payload = {
- 'user_id': 123,
- 'exp': int(time.time()) + 3600 # 1小时后过期
- }
- token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
- headers = {'Authorization': f'Bearer {token}'}
- resp = requests.get('https://api.example.com/protected', headers=headers)
复制代码 JWT也可使用RS256(RSA)非对称算法,此时需要提供公私钥对。
3.3 RSA非对称签名(私钥签名,公钥验证)
适用于支付回调、银行接口等高安全场景。- from cryptography.hazmat.primitives import hashes, serialization
- from cryptography.hazmat.primitives.asymmetric import padding
- import base64
- import requests
- def sign_with_rsa(private_key_pem, message):
- private_key = serialization.load_pem_private_key(private_key_pem, password=None)
- signature = private_key.sign(
- message.encode('utf-8'),
- padding.PKCS1v15(),
- hashes.SHA256()
- )
- return base64.b64encode(signature).decode('utf-8')
- # 使用示例
- with open('private_key.pem', 'rb') as f:
- private_key = f.read()
- message = 'GET /api/v1/resource'
- signature = sign_with_rsa(private_key, message)
- headers = {'X-Signature': signature, 'X-User': 'client_id'}
- resp = requests.get('https://api.example.com/resource', headers=headers)
复制代码 签名内容需严格按照API文档约定,可能是整个请求行或参数的摘要。
3.4 简单的MD5参数签名(老旧系统)
部分旧版接口仍使用此方式,安全性较低,建议尽快升级。- import hashlib
- import requests
- def md5_sign(params, salt):
- raw = f"key1={params['key1']}&key2={params['key2']}&salt={salt}"
- return hashlib.md5(raw.encode('utf-8')).hexdigest()
- params = {'amount': 100, 'order_id': '123'}
- params['sign'] = md5_sign(params, 'your_salt')
- resp = requests.post('https://api.example.com/pay', data=params)
复制代码 排序和拼接方式以API文档为准,通常按参数名顺序拼接。
4. 总结
本文从实际项目出发,展示了Python实现API接口签名的多种方法。核心示例基于HMAC-SHA256算法,通过拼接特定字符串并计算签名,可适配大多数RESTful服务。其他三种方式(OAuth 2.0、JWT、RSA、MD5)覆盖了从简单到高安全的典型场景。实际开发中,需仔细阅读API文档,明确签名规则、密钥获取方式以及过期机制。建议新系统优先使用HMAC-SHA256或RSA-SHA256,放弃MD5。掌握这些签名调用技巧,能极大提升开发效率和接口安全防护能力。 |