天天看點

Python 解密JWT驗證蘋果登入encoding=utf8

Python 解密JWT驗證蘋果登入

驗證蘋果登入,官方提供兩種驗證方法,一種是token,另一個種是code。這裡使用的是token

登入流程:

蘋果用戶端調用蘋果API,擷取到使用者的資訊,包括:

user_id

昵稱

identity_token

蘋果用戶端發送identity_token到服務端

服務端驗證identity_token是否合法,并解析資料,得到user_id。這個user_id和上面的user_id是一樣的

服務端檢查該user_id是否已注冊,如果是,傳回登入資訊。如果否,注冊。

驗證的原理:

蘋果會把使用者的資訊放在一個json裡面,然後使用私鑰對json簽名。

服務端調用蘋果API拿到公鑰,然後驗證簽名是否正确。

是以總的來說就是使用RSA私鑰簽名的算法來保證資料不會被篡改。

identity_token解析後包含幾部分内容

header。

例子:{u'alg': u'RS256', u'kid': u'86D88Kf'}

alg是加密算法類型

kid是使用的公鑰的id

payload或者claims 。也就是資料:

例子:

{

u'c_hash': u'HpjAKvLjivbJr9j9ZxfFxA',

u'aud': u'com.kugou.moe',

u'iss': u'

https://appleid.apple.com

',

u'email_verified': u'true',

u'nonce_supported': True,

u'exp': 1583829815,

u'auth_time': 1583829215,

u'iat': 1583829215,

u'email': u'[email protected]',

u'sub': u'001712.90358ddaa2294989b3b7c88b30086b37.0724' # 使用者唯一标志,相當于openid

}

aud 用戶端的報名。

exp。逾時時間,時間戳。目前時間如果大于這個時間,會報逾時錯誤

email 使用者的email

sub 使用者的user_id

demo

使用了python-jwt這個庫。安裝方法:pip install python-jwt

encoding=utf8

import requests

import logging

import python_jwt as jwt, jwcrypto.jwk as jwk

log = logging.getLogger('test')

class AppleLoginManager(object):

"""
蘋果登入
"""

@classmethod
def get_key(cls, kid):
    """
    通路apple 擷取公鑰。apple的接口會傳回很多公鑰的,根據jwt資料header的kid,找到對應的公鑰
    :param kid:
    :return: {
        "kty": "RSA",
        "kid": "eXaunmL",
        "use": "sig",
        "alg": "RS256",
        "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw",
        "e": "AQAB"
    }
    """
    ret = requests.get('https://appleid.apple.com/auth/keys')
    ret_json = ret.json()
    for key in ret_json['keys']:
        if key['kid'] == kid:
            return key
    log.error(u'[蘋果登入]找不到對應的kid %s %s' % (kid, ret_json))
    raise Exception('找不到對應的kid')

@classmethod
def verify_jwt(cls, token):
    """
    驗證jwt資料,傳回:
    {
      u'c_hash': u'HpjAKvivbJr9j9ZxfFxA',
      u'aud': u'com.test.moe',
      u'iss': u'https://appleid.apple.com',
      u'email_verified': u'true',
      u'nonce_supported': True,
      u'exp': 1583829815,
      u'auth_time': 1583829215,
      u'iat': 1583829215,
      u'email': u'[email protected]',
      u'sub': u'0017xx.9035989b3bxxxxx7c88b30086b37.xxx' # 使用者唯一标志,相當于openid
    }

    """
    header, claims = jwt.process_jwt(token)  # 擷取資訊,但是不驗證
    if claims['aud'] != 'com.test.test':  # 檢驗是不是自己的安裝包名
        log.error(u'[蘋果登入]aud異常 aud:%s  token:%s' % (claims['aud'], token))
        raise Exception(u'安裝包名異常')
    key_obj = AppleLoginManager.get_key(header['kid'])

    key = jwk.JWK(**key_obj)

    header, claims = jwt.verify_jwt(token, key, [key_obj['alg']], checks_optional=1)  # 擷取資訊并驗證
    return claims

@classmethod
def get_access_info(cls, token):
    """擷取授權資訊"""
    try:
        resp_json = AppleLoginManager.verify_jwt(token)
        return {
            "open_id": resp_json['sub'],
        }
    except:
        log.exception(u'蘋果登入異常,token:%s' % token)
        raise Exception('蘋果登入異常')
           

token = '''xxxx''' #前端傳過來的 identity_token

AppleLoginManager.get_access_info(token)

原文位址

https://www.cnblogs.com/Xjng/p/12464761.html