1

我有一个 python django rest 应用程序,我需要它来处理 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" 
        }}}

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

任何帮助或想法都会得到极大的赞赏

4

1 回答 1

1

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

 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 
于 2021-11-16T12:00:35.973 回答