• Python 解密JWT验证苹果登录


    1. 验证苹果登录,官方提供两种验证方法,一种是token,另一个种是code。这里使用的是token

    2. 登录流程:

      1. 苹果客户端调用苹果API,获取到用户的信息,包括:
        1. user_id
        2. 昵称
        3. identity_token
      2. 苹果客户端发送identity_token到服务端
      3. 服务端验证identity_token是否合法,并解析数据,得到user_id。这个user_id和上面的user_id是一样的
      4. 服务端检查该user_id是否已注册,如果是,返回登录信息。如果否,注册。
    3. 验证的原理:

      1. 苹果会把用户的信息放在一个json里面,然后使用私钥对json签名。
      2. 服务端调用苹果API拿到公钥,然后验证签名是否正确。
      3. 所以总的来说就是使用RSA私钥签名的算法来保证数据不会被篡改。
    4. identity_token解析后包含几部分内容

      1. header。
        1. 例子:{u'alg': u'RS256', u'kid': u'86D88Kf'}
        2. alg是加密算法类型
        3. kid是使用的公钥的id
      2. payload或者claims 。也就是数据:
        1. 例子:

        {
        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'huanghuixia5@163.com',
        u'sub': u'001712.90358ddaa2294989b3b7c88b30086b37.0724' # 用户唯一标志,相当于openid
        }
        ```
        1. aud 客户端的报名。
        2. exp。超时时间,时间戳。当前时间如果大于这个时间,会报超时错误
        3. email 用户的email
        4. 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'hua@163.com',
              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)
    
  • 相关阅读:
    移动端页面滚动性能调优
    外盘期货交易安全吗
    Vuetify的pagination功能踩坑
    oh-my-zsh自定义主题
    SSM整合
    Spring底层学习 1
    Node.js+Express day01
    JS+DOM conclusions
    tables+form+validation+Semantic HTML
    mybatis-利用association和collection实现一对一和一对多
  • 原文地址:https://www.cnblogs.com/Xjng/p/12464761.html
Copyright © 2020-2023  润新知