Django 中包含 JSON Web 签名的反序列化器 JSON 个对象。 App Store 服务器通知 responseBodyV2

Deserializer JSON objects containing JSON Web Signatures in Django. App Store Server Notifications responseBodyV2

我有一个 python django rest 应用程序,我需要它来处理 post 对 App Store 服务器通知的请求。

App Store 服务器通知负载的 v2 是 JSON Web 签名 (JWS) 格式,由 App Store 签名。其中包含的字段也采用 JSON Web 签名 (JWS) 格式,由 App Store 签名。我知道如何在程序上使用 python-jose 来处理这个问题,但我无法弄清楚如何以尽可能少的黑客攻击以优雅的方式将整个过程融入 Django 序列化程序。

数据可能是这样的:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub3RpZmljYXRpb25UeXBlIjoidHlwZSIsInN1YnR5cGUiOiJzdWJfVHlwZSIsIm5vdGlmaWNhdGlvblVVSUQiOiJzdHJpbmcgbm90aWZpY2F0aW9uVVVJRCIsImRhdGEiOnsiYXBwQXBwbGVJZCI6MTIzNCwiYnVuZGxlSWQiOiJhZmRzYXNkIiwiYnVuZGxlVmVyc2lvbiI6ImJ1bmRsZVZlcnNpb24iLCJlbnZpcm9ubWVudCI6ImVudmlyb25tZW50Iiwic2lnbmVkUmVuZXdhbEluZm8iOiJleUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKaGRYUnZVbVZ1WlhkUWNtOWtkV04wU1dRaU9pSjBaWE4wSUhSdmJHVnRJaXdpWVhWMGIxSmxibVYzVTNSaGRIVnpJam94TENKbGVIQnBjbUYwYVc5dVNXNTBaVzUwSWpvMExDSm5jbUZqWlZCbGNtbHZaRVY0Y0dseVpYTkVZWFJsSWpveE5qTTJOVE0xTVRReExDSnBjMGx1UW1sc2JHbHVaMUpsZEhKNVVHVnlhVzlrSWpwMGNuVmxMQ0p2Wm1abGNrbGtaVzUwYVdacFpYSWlPaUowWlhOMElIUnZiR1Z0SWl3aWIyWm1aWEpVZVhCbElqb3hMQ0p2Y21sbmFXNWhiRlJ5WVc1ellXTjBhVzl1U1dRaU9pSjBaWE4wSUhSdmJHVnRJaXdpY0hKcFkyVkpibU55WldGelpWTjBZWFIxY3lJNk1Td2ljSEp2WkhWamRFbGtJam9pZEdWemRDQjBiMnhsYlNJc0luTnBaMjVsWkVSaGRHVWlPakUyTXpZMU16VXhOREY5LnYwWW9YQUd0MTFPeVBXUk8zV2xTZDRiSWVtcVV6Q0ZJbFdjd0ZwcEI5TmMiLCJzaWduZWRUcmFuc2FjdGlvbkluZm8iOiJleUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKaGNIQkJZMk52ZFc1MFZHOXJaVzRpT2lKMFpYTjBJSFJ2YkdWdElpd2lZblZ1Wkd4bFNXUWlPaUp6WkdaellYTmtaaUlzSW1WNGNHbHlaWE5FWVhSbElqb3hOak0yTlRNMU1UUXhMQ0pwYmtGd2NFOTNibVZ5YzJocGNGUjVjR1VpT2lKMFpYTjBJSFJ2YkdWdElpd2lhWE5WY0dkeVlXUmxaQ0k2ZEhKMVpTd2liMlptWlhKSlpHVnVkR2xtYVdWeUlqb2lkR1Z6ZENCMGIyeGxiU0lzSW05bVptVnlWSGx3WlNJNk1UUTFMQ0p2Y21sbmFXNWhiRkIxY21Ob1lYTmxSR0YwWlNJNk1UWXpOalV6TlRFME1Td2liM0pwWjJsdVlXeFVjbUZ1YzJGamRHbHZia2xrSWpvaWRHVnpkQ0IwYjJ4bGJTSXNJbkJ5YjJSMVkzUkpaQ0k2SW5SbGMzUWdkRzlzWlcwaUxDSndkWEpqYUdGelpVUmhkR1VpT2pFMk16WTFNelV4TkRFc0luRjFZVzUwYVhSNUlqb3hORFVzSW5KbGRtOWpZWFJwYjI1RVlYUmxJam94TmpNMk5UTTFNVFF4TENKeVpYWnZZMkYwYVc5dVVtVmhjMjl1SWpveE5EVXNJbk5wWjI1bFpFUmhkR1VpT2pFMk16WTFNelV4TkRFc0luTjFZbk5qY21sd2RHbHZia2R5YjNWd1NXUmxiblJwWm1sbGNpSTZJblJsYzNRZ2RHOXNaVzBpTENKMGNtRnVjMkZqZEdsdmJrbGtJam9pZEdWemRDQjBiMnhsYlNJc0luUjVjR1VpT2lKMFpYTjBJSFJ2YkdWdElpd2lkMlZpVDNKa1pYSk1hVzVsU1hSbGJVbGtJam9pZEdWemRDQjBiMnhsYlNKOS5lbnlkTnVwd2txOTNYQ2dfeG5yYzNXTmtNNjM4NXpITnpoa0tqa3cyb3VrIn19.OgSJ4xE3r2Tw0Q4KcwPSD4YFo21uCLDgrKOtKOomijo

然后解码的点之间的部分看起来像

b'{"notificationType":"type","subtype":"sub_Type","notificationUUID":"string notificationUUID","data":{"appAppleId":1234,"bundleId":"afdsasd","bundleVersion":"bundleVersion","environment":"environment","signedRenewalInfo":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRvUmVuZXdQcm9kdWN0SWQiOiJ0ZXN0IHRvbGVtIiwiYXV0b1JlbmV3U3RhdHVzIjoxLCJleHBpcmF0aW9uSW50ZW50Ijo0LCJncmFjZVBlcmlvZEV4cGlyZXNEYXRlIjoxNjM2NTM1MTQxLCJpc0luQmlsbGluZ1JldHJ5UGVyaW9kIjp0cnVlLCJvZmZlcklkZW50aWZpZXIiOiJ0ZXN0IHRvbGVtIiwib2ZmZXJUeXBlIjoxLCJvcmlnaW5hbFRyYW5zYWN0aW9uSWQiOiJ0ZXN0IHRvbGVtIiwicHJpY2VJbmNyZWFzZVN0YXR1cyI6MSwicHJvZHVjdElkIjoidGVzdCB0b2xlbSIsInNpZ25lZERhdGUiOjE2MzY1MzUxNDF9.v0YoXAGt11OyPWRO3WlSd4bIemqUzCFIlWcwFppB9Nc","signedTransactionInfo":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBBY2NvdW50VG9rZW4iOiJ0ZXN0IHRvbGVtIiwiYnVuZGxlSWQiOiJzZGZzYXNkZiIsImV4cGlyZXNEYXRlIjoxNjM2NTM1MTQxLCJpbkFwcE93bmVyc2hpcFR5cGUiOiJ0ZXN0IHRvbGVtIiwiaXNVcGdyYWRlZCI6dHJ1ZSwib2ZmZXJJZGVudGlmaWVyIjoidGVzdCB0b2xlbSIsIm9mZmVyVHlwZSI6MTQ1LCJvcmlnaW5hbFB1cmNoYXNlRGF0ZSI6MTYzNjUzNTE0MSwib3JpZ2luYWxUcmFuc2FjdGlvbklkIjoidGVzdCB0b2xlbSIsInByb2R1Y3RJZCI6InRlc3QgdG9sZW0iLCJwdXJjaGFzZURhdGUiOjE2MzY1MzUxNDEsInF1YW50aXR5IjoxNDUsInJldm9jYXRpb25EYXRlIjoxNjM2NTM1MTQxLCJyZXZvY2F0aW9uUmVhc29uIjoxNDUsInNpZ25lZERhdGUiOjE2MzY1MzUxNDEsInN1YnNjcmlwdGlvbkdyb3VwSWRlbnRpZmllciI6InRlc3QgdG9sZW0iLCJ0cmFuc2FjdGlvbklkIjoidGVzdCB0b2xlbSIsInR5cGUiOiJ0ZXN0IHRvbGVtIiwid2ViT3JkZXJMaW5lSXRlbUlkIjoidGVzdCB0b2xlbSJ9.enydNupwkq93XCg_xnrc3WNkM6385zHNzhkKjkw2ouk"}}'

然后如果以 jws 格式编码的字段也按照上面提到的相同方式解码,它最终将如下所示:

{
"notificationType":"type",
"subtype":"sub_Type",
"notificationUUID":"string notificationUUID",
"data":
    {"appAppleId":1234,
     "bundleId":"afdsasd",
     "bundleVersion":"bundleVersion",
     "environment":"environment",
     "signedRenewalInfo":{
        "autoRenewProductId": "test tolem", 
        "autoRenewStatus": 1,
        "expirationIntent" : 4,
        "gracePeriodExpiresDate":  1636535141, 
        "isInBillingRetryPeriod": true,
        "offerIdentifier":  "test tolem", 
        "offerType": 1,
        "originalTransactionId": "test tolem", 
        "priceIncreaseStatus":  1,
        "productId": "test tolem",
        "signedDate": 1636535141,
        },
      "signedTransactionInfo":{
        "appAccountToken": "test tolem", 
        "bundleId": "sdfsasdf",
        "expiresDate" : 1636535141,
        "inAppOwnershipType":  "test tolem", 
        "isUpgraded": true,
        "offerIdentifier":  "test tolem", 
        "offerType": 145,
        "originalPurchaseDate": 1636535141,
        "originalTransactionId": "test tolem", 
        "productId":  "test tolem",
        "purchaseDate": 1636535141,
        "quantity": 145,
        "revocationDate": 1636535141,
        "revocationReason": 145,
        "signedDate": 1636535141,
        "subscriptionGroupIdentifier":  "test tolem", 
        "transactionId": "test tolem",
        "type":  "test tolem",
        "webOrderLineItemId": "test tolem" 
        }}}

这是我想要存储到我的数据库表中的内容

我们将不胜感激任何帮助或想法

对于可能处理相同问题的任何人 这就是我处理序列化的方式它看起来工作正常

 from .models import TransactionV2, RenewalInfoV2, 
    AppStoreDecodedPayloadV2, AppStoreDataV2
 from rest_framework import serializers
 from jose import jwt
 import base64
 import io
 from rest_framework.parsers import JSONParser
 import json
    
class PayloadField(serializers.Field):

    def to_internal_value(self, obj):
        content = obj.split('.')[1]
        jws_payload =  base64.b64decode(content)
        data = json.loads(jws_payload.decode())
        serializer = AppStoreDecodedPayloadSerializerV2(data = data)
        if serializer.is_valid():
            return serializer.validated_data

class SignedTransactionInfo(serializers.Field):

    def to_internal_value(self, obj):
        content = obj.split('.')[1]
        jws_payload =  base64.b64decode(content)
        data = json.loads(jws_payload.decode())
        serializer = TransactionV2Serializer(data = data)
        if serializer.is_valid():
            return serializer.validated_data


class SignedRenewalInfoField(serializers.Field):

    def to_internal_value(self, obj):
        content = obj.split('.')[1]
        algorithm = obj.split('.')[0]
        secret = obj.split('.')[2]

        jws_payload =  base64.b64decode(content)
        jws_payload_algo = base64.b64decode(algorithm)
       
        algo = json.loads(jws_payload_algo.decode())
        data = json.loads(jws_payload.decode())
        serializer = RenewSerializer(data = data)
        if serializer.is_valid():
            return serializer.validated_data


class AppStoreNotificationSerializerReuturnV2(serializers.Serializer):
    signedPayload =  PayloadField()

    def create(self, validated_data):
        app_store_decoded_payload_data = validated_data.pop('signedPayload')
        app_store_sub = AppStoreDecodedPayloadSerializerV2.create(app_store_decoded_payload_data)
        return app_store_sub

class AppStoreDecodedPayloadDataSerializer(serializers.Serializer):
    appAppleId = serializers.IntegerField()
    bundleId = serializers.CharField()
    bundleVersion = serializers.CharField()
    environment = serializers.CharField()
    signedRenewalInfo = SignedRenewalInfoField()
    signedTransactionInfo = SignedTransactionInfo()
    
    def create(**decoded_payload_data):
        renewall_data = decoded_payload_data.pop('signedRenewalInfo')
        transaction_data = decoded_payload_data.pop('signedTransactionInfo')
        app_store_data = AppStoreDataV2.objects.create(**decoded_payload_data)
        TransactionV2.objects.create(**transaction_data ,app_store_data = app_store_data)
        RenewalInfoV2.objects.create(**renewall_data, app_store_data = app_store_data)
        return app_store_data


class AppStoreDecodedPayloadSerializerV2(serializers.Serializer):
    notificationType = serializers.CharField()
    subtype = serializers.CharField()
    notificationUUID = serializers.CharField()
    data = AppStoreDecodedPayloadDataSerializer()
    
    def create(app_store_decoded_payload):
        app_store_decoded_payload_data = app_store_decoded_payload.pop('data')
        decoded_payload = AppStoreDecodedPayloadV2.objects.create(**app_store_decoded_payload)
        app_store_decoded_payload =AppStoreDecodedPayloadDataSerializer.create(**app_store_decoded_payload_data, app_store_decoded_payload = decoded_payload)
        return app_store_decoded_payload


class TransactionV2Serializer(serializers.ModelSerializer):

    class Meta:
        fields = '__all__'
        model = TransactionV2


class RenewSerializer(serializers.ModelSerializer):

    class Meta:
        fields = '__all__'
        model = RenewalInfoV2