ali.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import logging
  4. import urllib
  5. from typing import TYPE_CHECKING
  6. from apilib.utils_url import add_query
  7. from apps.web.core import AlipayMixin
  8. from library.alipay import AliException
  9. from .base import AuthBridge
  10. if TYPE_CHECKING:
  11. from apps.web.core.models import AliApp
  12. logger = logging.getLogger(__name__)
  13. class AlipayAuthBridge(AuthBridge, AlipayMixin):
  14. auth_code_key = 'auth_code'
  15. # 静默跳转,无需用户点击授权
  16. AUTH_SCOPE_BASE = 'auth_base'
  17. # 需要用户显式点击授权
  18. AUTH_SCOPE_USER = 'auth_user'
  19. # 测试网关oauth
  20. DEV_ALIPAY_OAUTH_URL = \
  21. 'https://openauth.alipaydev.com/oauth2/publicAppAuthorize.htm?' + \
  22. 'app_id={appid}&scope={scope}&redirect_uri={encoded_return_uri}&state={state}'
  23. # 生产环境网关oauth
  24. PRODUCTION_ALIPAY_OAUTH_URL = \
  25. 'https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?' + \
  26. 'app_id={appid}&scope={scope}&redirect_uri={encoded_return_uri}&state={state}'
  27. def __init__(self, app):
  28. # type: (AliApp)->None
  29. super(AlipayAuthBridge, self).__init__(app)
  30. if self.debug:
  31. self._auth_gateway_tmpl = self.DEV_ALIPAY_OAUTH_URL
  32. else:
  33. self._auth_gateway_tmpl = self.PRODUCTION_ALIPAY_OAUTH_URL
  34. def generate_auth_url(self, redirect_uri, payload, scope):
  35. """
  36. 参数名 是否必须 描述
  37. app_id 是 开发者应用的app_id
  38. scope 是 接口权限值,目前只支持auth_user和auth_base两个值
  39. redirect_uri 是 回调页面
  40. state 否
  41. 商户自定义参数,用户授权后,
  42. 重定向到redirect_uri时会原样回传给商户。
  43. 为防止CSRF攻击,建议开发者请求授权时传入state参数,
  44. 该参数要做到既不可预测,又可以证明客户端和当前第三方网站的登录认证状态存在关联。
  45. ```
  46. 关于scope的说明:
  47. auth_base:以auth_base为scope发起的网页授权,是用来获取进入页面的用户的userId的,
  48. 并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(通常是业务页面)。
  49. auth_user:以auth_user为scope发起的网页授权,是用来获取用户的基本信息的(比如头像、昵称等)。
  50. 但这种授权需要用户手动同意,用户同意后,就可在授权后获取到该用户的基本信息。若想获取用户信息,scope的值中需要有该值存在,
  51. 如scope=auth_user, auth_base。
  52. ```
  53. :return: <str>url
  54. """
  55. logger.debug('generate_auth_callback_url enter. bridge = {}, '
  56. 'callback url = {}; payload = {}'.format(repr(self), redirect_uri, payload))
  57. callback_url = add_query(redirect_uri, {'payload': payload})
  58. encoded_return_uri = urllib.quote_plus(callback_url)
  59. result = self._auth_gateway_tmpl.format(appid = self.appid,
  60. scope = scope,
  61. encoded_return_uri = encoded_return_uri,
  62. state = '')
  63. logger.debug('generate_auth_callback_url success. result = {}'.format(str(result)))
  64. return result
  65. def generate_auth_url_base_scope(self,
  66. redirect_uri,
  67. payload = None):
  68. return self.generate_auth_url(payload = payload,
  69. scope = self.AUTH_SCOPE_BASE,
  70. redirect_uri = redirect_uri)
  71. def generate_auth_url_user_scope(self,
  72. redirect_uri,
  73. payload = None):
  74. return self.generate_auth_url(payload = payload,
  75. scope = self.AUTH_SCOPE_USER,
  76. redirect_uri = redirect_uri)
  77. def get_user_info(self, auth_code, refresh_token = None):
  78. logger.debug(
  79. 'get alipay user info by bridge = {}, auth_code = {}, refresh_token = {}'.format(
  80. repr(self), auth_code, refresh_token))
  81. auth_token = self.client.api_alipay_system_oauth_token(auth_code = auth_code, refresh_token = refresh_token)
  82. response = self.client.api_alipay_user_info_share(auth_token = auth_token['access_token'])
  83. logger.debug('alipay user info = {}'.format(response))
  84. if response['code'] == u'10000':
  85. result = {
  86. 'openId': response.get('user_id'),
  87. 'avatar': response.get('avatar', ''),
  88. 'nickname': response.get('nick_name', ''),
  89. 'province': response.get('province', ''),
  90. 'city': response.get('city', ''),
  91. 'extra': {
  92. 'is_student_certified': response.get('is_student_certified', ''),
  93. 'user_type': response.get('user_type', ''),
  94. 'user_status': response.get('user_status', ''),
  95. 'is_certified': response.get('is_certified', '')
  96. }
  97. }
  98. sex = response.get('gender', 'unknown').lower()
  99. if sex == 'm':
  100. result['sex'] = 1
  101. elif sex == 'f':
  102. result['sex'] = 2
  103. else:
  104. result['sex'] = 0
  105. return result
  106. else:
  107. raise AliException(
  108. errCode = response['code'],
  109. errMsg = response['msg'],
  110. client = self.client)