完全可行,但需前后端密钥格式、PKCS#1 v1.5填充严格一致;前端禁用encryptLong分段,单次加密≤117字节;公私钥须为PEM格式且未加密;Django中间件解密时须重置_request._body和_request._post。

前端用 jsencrypt 加密,后端用 cryptography 解密是否可行
完全可行,但要注意密钥格式和填充方式必须严格一致。jsencrypt 默认使用 PKCS#1 v1.5 填充,后端 cryptography 必须显式指定 padding.PKCS1v15(),否则解密失败并抛出 ValueError: Encryption/decryption failed.。
常见错误是前端用 encryptLong 分段加密(自动切块),而后端只调用一次 private_key.decrypt() —— 这会导致解密失败或乱码。正确做法是:前端不拆分,单次加密不超过 117 字节(2048 位 RSA);或后端手动分段解密(不推荐,易出错)。
- 前端 RSA 公钥需转成 PEM 格式字符串(含
-----BEGIN PUBLIC KEY-----头尾),不能直接用 JWK 或 base64 纯模幂值 - 后端私钥必须是 PEM 格式且未加密(即无 passphrase),否则
load_pem_private_key()会报ValueError: Could not deserialize key data. - 加密前建议对原始参数 JSON 序列化 + UTF-8 编码,避免中文乱码;解密后需用
.decode('utf-8')转回字符串
Django 中间件拦截 POST 参数并自动解密
不要在视图里重复写解密逻辑。用中间件统一处理最稳妥,但注意:Django 中间件的 process_request 拿不到已解析的 request.POST(因为它是惰性解析的),必须主动读取 request.body 并替换为解密后的字典。
关键点在于解密后要重置 request._body 和 request._post,否则后续视图仍读到原始密文。示例代码核心片段:
立即学习“Python免费学习笔记(深入)”;
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.serialization import load_pem_private_key
<p>class RSADecryptMiddleware:
def <strong>init</strong>(self, get_response):
self.get_response = get_response
with open('/path/to/private_key.pem', 'rb') as f:
self.private_key = load_pem_private_key(f.read(), password=None)</p><pre class='brush:python;toolbar:false;'>def __call__(self, request):
if request.method == 'POST' and request.content_type == 'application/json':
try:
encrypted_data = json.loads(request.body).get('data')
if encrypted_data:
decrypted = self.private_key.decrypt(
base64.b64decode(encrypted_data),
padding.PKCS1v15()
)
# 替换原始 body 为解密后的内容
request._body = decrypted
request._post = QueryDict(decrypted, encoding='utf-8')
except Exception as e:
# 记录日志,但不拦截,让视图自行处理异常
pass
return self.get_response(request)</pre>- 务必检查
request.content_type,避免对 form-data、文件上传等非 JSON 请求误操作 - 不要覆盖
request.POST属性(它是只读 property),而是设置request._post和request._body - 中间件顺序很重要:必须放在
SessionMiddleware之后、CommonMiddleware之前,否则request.body可能已被消耗
如何安全传递 RSA 公钥给前端
公钥本身可公开,但必须防篡改。最简单可靠的方式是:后端在登录成功响应中返回公钥 PEM 字符串(去头尾换行,base64 编码),前端缓存并用于后续请求加密。
绝对不要把公钥硬编码在 JS 文件里,也不要用 API 接口单独暴露(容易被爬或注入)。如果必须预置,至少用 Django 模板渲染进 HTML 的 <script> 标签内,并开启 CSP 防止 XSS 注入伪造公钥。
- 公钥传输时建议加签名(如用服务端私钥对公钥内容做 HMAC-SHA256),前端校验通过再使用
- 前端每次加密前应校验公钥长度(如
key.length === 2048),防止被中间人替换成弱密钥 - 公钥过期策略:可配合 JWT 的
jti或自定义 header 字段,服务端拒绝使用已废弃公钥加密的请求
为什么不用 JWT 或 HTTPS 就要自己加 RSA
HTTPS 只保证传输层加密,不解决「参数在服务端被日志明文记录」或「内部微服务间转发时被窃听」的问题。RSA 加密是应用层保护,确保敏感字段(如身份证号、银行卡号)从进入 Django 到落库全程不可见。
但代价明显:性能开销大(RSA 解密比 AES 慢百倍以上)、无法加密大字段、密钥轮换复杂。真实项目中更常见的做法是:仅对极少数高敏字段(如 id_card、bank_account)做 RSA 加密,其余走 HTTPS + 字段级 AES(用 KMS 托管密钥)。
最容易被忽略的是日志脱敏——即使加了 RSA,如果在中间件解密后还把 request.POST 原样打到日志里,等于白做。必须在日志中间件中过滤掉已解密的敏感键名。
















