wechat.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import logging
  4. from typing import TYPE_CHECKING, Optional
  5. from apilib.systypes import IterConstant
  6. from apilib.utils_url import add_query
  7. from apps.web.constant import AppPlatformType
  8. from apps.web.core import WechatMixin
  9. from apps.web.core.auth.base import AuthBridge
  10. from library.wechatpy.oauth import WeChatOAuth
  11. from library.wechatbase.exceptions import WechatOAuthDummyException
  12. if TYPE_CHECKING:
  13. from apps.web.core.models import WechatPayApp, WechatAuthApp, WechatManagerApp, WechatUserManagerApp
  14. logger = logging.getLogger(__name__)
  15. class WechatAuthScope(IterConstant):
  16. # 静默跳转,无需用户点击授权
  17. AUTH_SCOPE_BASE = 'snsapi_base'
  18. # 需要用户显式点击授权
  19. AUTH_SCOPE_USER = 'snsapi_userinfo'
  20. class WechatAuthBridge(AuthBridge, WechatMixin):
  21. auth_code_key = 'code'
  22. def __init__(self, app):
  23. # type: (Optional[WechatAuthApp, WechatManagerApp, WechatUserManagerApp, WechatPayApp])->None
  24. super(WechatAuthBridge, self).__init__(app)
  25. self.__gateway_type__ = AppPlatformType.WECHAT
  26. @property
  27. def __client__(self):
  28. return WeChatOAuth(self.appid, self.secret)
  29. def authorize(self, auth_code):
  30. logger.debug('{} authorize enter. code = {}'.format(repr(self), auth_code))
  31. if auth_code is None:
  32. logger.error('{} fail to authorize because code is null'.format(repr(self)))
  33. return None
  34. try:
  35. openId = self.client.get_oauth_token(auth_code).get('openid')
  36. logger.debug('{} success to authorize. open id = {}'.format(repr(self), openId))
  37. return openId
  38. except Exception as e:
  39. logger.error('{} fail to authorize because of exception. code = {}'.format(repr(self), auth_code))
  40. logger.exception(e)
  41. return None
  42. def get_user_info(self, auth_code):
  43. # type:(str)->dict
  44. auth_token = self.client.get_oauth_token(auth_code)
  45. logger.debug(
  46. '{bridge} get auth token. auth_token = {auth_token}'.format(bridge = repr(self), auth_token = auth_token))
  47. payload = {
  48. 'scope': auth_token['scope']
  49. }
  50. if 'snsapi_userinfo' in auth_token['scope']:
  51. if 'is_snapshotuser' in auth_token and auth_token['is_snapshotuser'] == 1:
  52. raise WechatOAuthDummyException()
  53. response = self.client.get_user_info(
  54. openid = auth_token['openid'], access_token = auth_token['access_token'])
  55. logger.debug(
  56. '{bridge} get user info. user info = {user_info}'.format(bridge = repr(self), user_info = response))
  57. payload.update({
  58. 'openId': response.get('openid'),
  59. 'nickname': response.get('nickname', ''),
  60. 'province': response.get('province', ''),
  61. 'city': response.get('city', ''),
  62. 'country': response.get('country', ''),
  63. 'avatar': response.get('headimgurl', ''),
  64. 'sex': int(response.get('sex', 0)),
  65. 'unionid': response.get('unionid', ''),
  66. })
  67. return payload
  68. else:
  69. payload.update({
  70. 'openId': auth_token['openid']
  71. })
  72. return payload
  73. def generate_auth_url(self, redirect_uri, payload = '', scope = WechatAuthScope.AUTH_SCOPE_BASE):
  74. """
  75. 生成授权url
  76. :param redirect_uri:
  77. :param payload:
  78. :param scope:
  79. :return:
  80. """
  81. assert redirect_uri is not None
  82. if payload:
  83. redirect_uri = add_query(redirect_uri, {'payload': payload})
  84. return WeChatOAuth(self.appid, self.secret, scope = scope).authorize_url(redirect_uri = redirect_uri)
  85. def generate_auth_url_base_scope(self, redirect_uri, payload = ''):
  86. """
  87. 生成微信跳转url base范围,只能获取openId
  88. :param redirect_uri:
  89. :param payload:
  90. :return:
  91. """
  92. logger.debug('%r redirect scope = %s, redirect_uri = %s; payload = %s' % (
  93. self, WechatAuthScope.AUTH_SCOPE_BASE, redirect_uri, payload))
  94. return self.generate_auth_url(redirect_uri = redirect_uri,
  95. payload = payload,
  96. scope = WechatAuthScope.AUTH_SCOPE_BASE)
  97. def generate_auth_url_user_scope(self, redirect_uri, payload = ''):
  98. """
  99. 生成微信跳转url user范围,只能获取openId
  100. :param redirect_uri:
  101. :param payload:
  102. :return:
  103. """
  104. logger.debug('%r redirect scope = %s, redirect_uri = %s; state = %s' % (
  105. self, WechatAuthScope.AUTH_SCOPE_USER, redirect_uri, payload))
  106. return self.generate_auth_url(redirect_uri = redirect_uri,
  107. payload = payload,
  108. scope = WechatAuthScope.AUTH_SCOPE_USER)
  109. def jscode2session(self, js_code):
  110. return self.client.jscode2session(js_code)