wechat.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. # -*- coding: utf-8 -*-
  2. #!/usr/bin/env python
  3. import os
  4. import time
  5. import json
  6. import base64
  7. import urllib
  8. import hashlib
  9. import logging
  10. logger = logging.getLogger(__name__)
  11. ACCESS_TOKEN = os.environ.get('FAKE_ACCESS_TOKEN', "N2L7KXa084WvelONYjkJ_traBMCCvy_UKmpUUzlrQ0EA2yNp3Iz6eSUrRG0bhaR_viswd50vDuPkY5nG43d"
  12. "1gbm-olT2KRMxOsVE08RfeD9lvK9lMguNG9kpIkKGZEjIf8Jv2m9fFhf8bnNa-yQH3g")
  13. OPEN_ID = os.environ.get('TEST_OPEN_ID', "test-ttttttttt_eeeee-sssssst")
  14. class FakeWechatApp(object):
  15. appid = os.environ.get('FAKE_WECHAT_APPID', 'fakewechatappid')
  16. secret = os.environ.get('FAKE_WECHAT_SECRET', 'fakewechatsecret')
  17. class FakeWechatAuthClient(object):
  18. def get_oauth_token(self, auth_code):
  19. return {
  20. 'access_token': ACCESS_TOKEN,
  21. 'openId': ''
  22. }
  23. def jsapi_sign(self, url):
  24. return {
  25. 'sign': '',
  26. 'noncestr': ''
  27. }
  28. @property
  29. def jsapi_ticket(self):
  30. return ''
  31. def get_user_info(self, **kwargs):
  32. return ''
  33. class FakeWechatAuthBridge(object):
  34. # 静默跳转,无需用户点击授权
  35. AUTH_SCOPE_BASE = 'snsapi_base'
  36. # 需要用户显式点击授权
  37. AUTH_SCOPE_USER = 'snsapi_userinfo'
  38. PROD_AUTH_CALLBACK_URL_TMPL = \
  39. '{redirect_uri}?scope={scope}&state={state}&connect_redirect=1#wechat_redirect'
  40. GET_USER_OPENID_LIST_URL = 'https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID'
  41. TOKEN_CACHE_KEY = 'access_token_from_{appid}_{code}'
  42. GZH_TOKEN_CACHE_KEY = 'gzh_access_token_from_{appId}'
  43. def __init__(self, app, redirect_uri=None):
  44. self._appid = app['appid']
  45. self._secret = app['secret']
  46. self._redirect_uri = redirect_uri
  47. def __repr__(self):
  48. return '<FakeWechatAuthBridge(appid=%s, secret=%s, redirect_uri=%s)>' \
  49. % (self._appid, self._secret, self._redirect_uri)
  50. @property
  51. def appid(self):
  52. return self._appid
  53. @property
  54. def secret(self):
  55. return self._secret
  56. @property
  57. def client(self):
  58. return FakeWechatAuthClient()
  59. def authorize(self, auth_code):
  60. """微信oauth授权,经销商和用户都有涉及"""
  61. if auth_code is None:
  62. logger.debug('[wechat authorize]failure. code is null. bridge = %s' % repr(self))
  63. return None
  64. try:
  65. openId = self.client.get_oauth_token(auth_code).get('openid')
  66. logger.debug('[wechat authorize]success. code = %s. bridge = %s; openid = %s' % (auth_code, repr(self), openId))
  67. return openId
  68. except Exception, e:
  69. logger.exception('[wechat authorize]exception, bridge = %s; error = %s' % (repr(self), str(e),))
  70. return None
  71. def need_authorize(self, appId):
  72. if self.appid == appId:
  73. return False
  74. else:
  75. return True
  76. def get_user_info(self, auth_code):
  77. logger.debug('[wechat get_user_info]. code = %s' % auth_code)
  78. rv = self.client.get_oauth_token(auth_code)
  79. return self.client.get_user_info(openid=rv['openid'], access_token=rv['access_token'])
  80. def _sign(self, payload):
  81. raw = [(k, payload[k]) for k in sorted(payload.keys())]
  82. s = '&'.join('='.join(kv) for kv in raw if kv[1])
  83. return hashlib.sha1(s.encode("utf-8")).hexdigest().lower()
  84. def generate_js_auth_signature(self, url='/app/index.html'):
  85. """
  86. 生成签名信息,主要供经销商获取扫码权限
  87. :return:
  88. """
  89. signDict = self.client.jsapi_sign(url=url)
  90. return {
  91. 'signature': signDict['sign'],
  92. 'appId': self.appid,
  93. 'jsapi_ticket': self.client.jsapi_ticket,
  94. 'url': url,
  95. 'timestamp': str(int(time.time())),
  96. 'nonceStr': signDict['noncestr']
  97. }
  98. def generate_auth_url(self, redirect_uri, state='', scope=AUTH_SCOPE_BASE):
  99. """
  100. 生成授权url
  101. :param redirect_uri:
  102. :param state:
  103. :param scope:
  104. :return:
  105. """
  106. return self.PROD_AUTH_CALLBACK_URL_TMPL.format(
  107. redirect_uri=redirect_uri,
  108. state=state,
  109. scope=scope)
  110. def generate_auth_url_base_scope(self, redirect_uri, state=''):
  111. logger.debug('response redirect. bridge = %s, type = %s, redirect_uri = %s; state = %s' % (
  112. repr(self), self.AUTH_SCOPE_BASE, redirect_uri, state))
  113. return self.generate_auth_url(redirect_uri=redirect_uri, state=state, scope=self.AUTH_SCOPE_BASE)
  114. def generate_auth_url_user_scope(self, redirect_uri, state=''):
  115. logger.debug('response redirect. bridge = %s, type = %s, redirect_uri = %s; state = %s' % (
  116. repr(self), self.AUTH_SCOPE_USER, redirect_uri, state))
  117. return self.generate_auth_url(redirect_uri=redirect_uri, state=state, scope=self.AUTH_SCOPE_USER)
  118. @staticmethod
  119. def encode_state(state):
  120. return base64.b64encode(json.dumps(state))
  121. @staticmethod
  122. def parse_state(state_str):
  123. return json.loads(base64.b64decode(state_str))
  124. def is_subscribe_gzh(self, openId):
  125. return True
  126. __all__ = ['FakeWechatAuthBridge']