def __init__(self, app_id, component_appid=None, component_access_token=None, redirect_uri=None, scope='snsapi_base', state='', component=None): """ :param app_id: 微信公众号 app_id :param component: WeChatComponent """ self._http = requests.Session() self.app_id = app_id self.component = component if self.component is None: warnings.warn( 'cannot found `component` param of `ComponentOAuth` `__init__` method,' 'Use `WeChatComponent.get_component_oauth` instead', DeprecationWarning, stacklevel=2) self.component = ObjectDict({ 'component_appid': component_appid, 'access_token': component_access_token }) if redirect_uri is not None: warnings.warn( 'found `redirect_uri` param of `ComponentOAuth` `__init__` method,' 'Use `ComponentOAuth.get_authorize_url` instead', DeprecationWarning, stacklevel=2) self.authorize_url = self.get_authorize_url( redirect_uri, scope, state)
def __get__(self, instance, instance_type=None): if instance is not None: value = instance._data.get(self.attr_name) if value is None: value = copy.deepcopy(self.field.default) instance._data[self.attr_name] = value if isinstance(value, dict): value = ObjectDict(value) if value and not isinstance(value, (dict, list, tuple)) and callable(self.field.converter): value = self.field.converter(value) return value return self.field
def __init__(self, app_id, component_appid=None, component_access_token=None, redirect_uri=None, scope='snsapi_base', state='', component=None): """ :param app_id: 微信公众号 app_id :param component: WeChatComponent """ self.app_id = app_id self.component = component if self.component is None: warnings.warn('cannot found `component` param of `ComponentOAuth` `__init__` method,' 'Use `WeChatComponent.get_component_oauth` instead', DeprecationWarning, stacklevel=2) self.component = ObjectDict({'component_appid': component_appid, 'access_token': component_access_token}) if redirect_uri is not None: warnings.warn('found `redirect_uri` param of `ComponentOAuth` `__init__` method,' 'Use `ComponentOAuth.get_authorize_url` instead', DeprecationWarning, stacklevel=2) self.authorize_url = self.get_authorize_url(redirect_uri, scope, state)
def test_object_dict(self): obj = ObjectDict() self.assertTrue(obj.xxx is None) obj.xxx = 1 self.assertEqual(1, obj.xxx)
class ComponentOAuth(object): """ 微信开放平台 代公众号 OAuth 网页授权 详情请参考 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318590 """ API_BASE_URL = 'https://api.weixin.qq.com/' OAUTH_BASE_URL = 'https://open.weixin.qq.com/connect/' def __init__(self, app_id, component_appid=None, component_access_token=None, redirect_uri=None, scope='snsapi_base', state='', component=None): """ :param app_id: 微信公众号 app_id :param component: WeChatComponent """ self._http = requests.Session() self.app_id = app_id self.component = component if self.component is None: warnings.warn( 'cannot found `component` param of `ComponentOAuth` `__init__` method,' 'Use `WeChatComponent.get_component_oauth` instead', DeprecationWarning, stacklevel=2) self.component = ObjectDict({ 'component_appid': component_appid, 'access_token': component_access_token }) if redirect_uri is not None: warnings.warn( 'found `redirect_uri` param of `ComponentOAuth` `__init__` method,' 'Use `ComponentOAuth.get_authorize_url` instead', DeprecationWarning, stacklevel=2) self.authorize_url = self.get_authorize_url( redirect_uri, scope, state) def get_authorize_url(self, redirect_uri, scope='snsapi_base', state=''): """ :param redirect_uri: 重定向地址,需要urlencode,这里填写的应是服务开发方的回调地址 :param scope: 可选,微信公众号 OAuth2 scope,默认为 ``snsapi_base`` :param state: 可选,重定向后会带上state参数,开发者可以填写任意参数值,最多128字节 """ redirect_uri = quote(redirect_uri, safe=b'') url_list = [ self.OAUTH_BASE_URL, 'oauth2/authorize?appid=', self.app_id, '&redirect_uri=', redirect_uri, '&response_type=code&scope=', scope, ] if state: url_list.extend(['&state=', state]) url_list.extend([ '&component_appid=', self.component.component_appid, ]) url_list.append('#wechat_redirect') return ''.join(url_list) def fetch_access_token(self, code): """获取 access_token :param code: 授权完成跳转回来后 URL 中的 code 参数 :return: JSON 数据包 """ res = self._get('sns/oauth2/component/access_token', params={ 'appid': self.app_id, 'component_appid': self.component.component_appid, 'component_access_token': self.component.access_token, 'code': code, 'grant_type': 'authorization_code', }) self.access_token = res['access_token'] self.open_id = res['openid'] self.refresh_token = res['refresh_token'] self.expires_in = res['expires_in'] self.scope = res['scope'] return res def refresh_access_token(self, refresh_token): """刷新 access token :param refresh_token: OAuth2 refresh token :return: JSON 数据包 """ res = self._get('sns/oauth2/component/refresh_token', params={ 'appid': self.app_id, 'grant_type': 'refresh_token', 'refresh_token': refresh_token, 'component_appid': self.component.component_appid, 'component_access_token': self.component.access_token, }) self.access_token = res['access_token'] self.open_id = res['openid'] self.refresh_token = res['refresh_token'] self.expires_in = res['expires_in'] self.scope = res['scope'] return res def get_user_info(self, openid=None, access_token=None, lang='zh_CN'): """ 获取用户基本信息(需授权作用域为snsapi_userinfo) 如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。 :param openid: 可选,微信 openid,默认获取当前授权用户信息 :param access_token: 可选,access_token,默认使用当前授权用户的 access_token :param lang: 可选,语言偏好, 默认为 ``zh_CN`` :return: JSON 数据包 """ openid = openid or self.open_id access_token = access_token or self.access_token return self._get('sns/userinfo', params={ 'access_token': access_token, 'openid': openid, 'lang': lang }) def _request(self, method, url_or_endpoint, **kwargs): if not url_or_endpoint.startswith(('http://', 'https://')): url = '{base}{endpoint}'.format(base=self.API_BASE_URL, endpoint=url_or_endpoint) else: url = url_or_endpoint if isinstance(kwargs.get('data', ''), dict): body = json.dumps(kwargs['data'], ensure_ascii=False) body = body.encode('utf-8') kwargs['data'] = body res = self._http.request(method=method, url=url, **kwargs) try: res.raise_for_status() except requests.RequestException as reqe: raise WeChatOAuthException(errcode=None, errmsg=None, client=self, request=reqe.request, response=reqe.response) return self._handle_result(res, method=method, url=url, **kwargs) def _handle_result(self, res, method=None, url=None, **kwargs): result = json.loads(res.content.decode('utf-8', 'ignore'), strict=False) if 'errcode' in result: result['errcode'] = int(result['errcode']) if 'errcode' in result and result['errcode'] != 0: errcode = result['errcode'] errmsg = result.get('errmsg', errcode) if self.component.auto_retry and errcode in ( WeChatErrorCode.INVALID_CREDENTIAL.value, WeChatErrorCode.INVALID_ACCESS_TOKEN.value, WeChatErrorCode.EXPIRED_ACCESS_TOKEN.value): logger.info( 'Component access token expired, fetch a new one and retry request' ) self.component.fetch_access_token() kwargs['params'][ 'component_access_token'] = self.component.access_token return self._request(method=method, url_or_endpoint=url, **kwargs) elif errcode == WeChatErrorCode.OUT_OF_API_FREQ_LIMIT.value: # api freq out of limit raise APILimitedException(errcode, errmsg, client=self, request=res.request, response=res) else: raise WeChatComponentOAuthException(errcode, errmsg, client=self, request=res.request, response=res) return result def _get(self, url, **kwargs): return self._request(method='get', url_or_endpoint=url, **kwargs)
def wechat(request): """ 此地址为响应微信发送的Token验证,验证服务器配置是否正确 :param request: :return: """ signature = request.GET.get('signature', 'c58469c4151fac046efe180b277c51b1e5b563d3') timestamp = request.GET.get('timestamp', '1451138472') nonce = request.GET.get('nonce', '1432579014') echo_str = request.GET.get('echostr', '2691756735856574460') encrypt_type = request.args.get('encrypt_type', '') msg_signature = request.args.get('msg_signature', '') print('signature:', signature) print('timestamp: ', timestamp) print('nonce:', nonce) print('echo_str:', echo_str) print('encrypt_type:', encrypt_type) print('msg_signature:', msg_signature) try: check_signature(settings.WECHAT_TOKEN, signature, timestamp, nonce) except InvalidSignatureException: return HttpResponseForbidden() if request.method == 'GET': return echo_str else: print('Raw message: \n%s' % request.data) crypto = WeChatCrypto(settings.WECHAT_TOKEN, settings.EncodingAESKey, settings.WECHAT_APPID) try: msg = crypto.decrypt_message( request.data, msg_signature, timestamp, nonce ) print('Descypted message: \n%s' % msg) except (InvalidSignatureException, InvalidAppIdException): return HttpResponseForbidden() msg = parse_message(msg) if msg.type == 'text': reply = create_reply(msg.content, msg) elif msg.type == 'image': reply = ArticlesReply(message=msg) # simply use dict as article reply.add_article({ 'title': 'test', 'description': 'test', 'image': 'image url', 'url': 'url' }) # or you can use ObjectDict article = ObjectDict() article.title = 'test' article.description = 'test' article.image = 'image url' article.url = 'url' reply.add_article(article) # reply = create_reply([article], msg) else: reply = create_reply('Sorry, can not handle this for now', msg) msg= crypto.encrypt_message( reply.render(), nonce, timestamp) print(msg) return HttpResponse(msg)
class ComponentOAuth(object): """ 微信开放平台 代公众号 OAuth 网页授权 详情请参考 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318590 """ _http = requests.Session() API_BASE_URL = 'https://api.weixin.qq.com/' OAUTH_BASE_URL = 'https://open.weixin.qq.com/connect/' def __init__(self, app_id, component_appid=None, component_access_token=None, redirect_uri=None, scope='snsapi_base', state='', component=None): """ :param app_id: 微信公众号 app_id :param component: WeChatComponent """ self.app_id = app_id self.component = component if self.component is None: warnings.warn('cannot found `component` param of `ComponentOAuth` `__init__` method,' 'Use `WeChatComponent.get_component_oauth` instead', DeprecationWarning, stacklevel=2) self.component = ObjectDict({'component_appid': component_appid, 'access_token': component_access_token}) if redirect_uri is not None: warnings.warn('found `redirect_uri` param of `ComponentOAuth` `__init__` method,' 'Use `ComponentOAuth.get_authorize_url` instead', DeprecationWarning, stacklevel=2) self.authorize_url = self.get_authorize_url(redirect_uri, scope, state) def get_authorize_url(self, redirect_uri, scope='snsapi_base', state=''): """ :param redirect_uri: 重定向地址,需要urlencode,这里填写的应是服务开发方的回调地址 :param scope: 可选,微信公众号 OAuth2 scope,默认为 ``snsapi_base`` :param state: 可选,重定向后会带上state参数,开发者可以填写任意参数值,最多128字节 """ redirect_uri = quote(redirect_uri, safe=b'') url_list = [ self.OAUTH_BASE_URL, 'oauth2/authorize?appid=', self.app_id, '&redirect_uri=', redirect_uri, '&response_type=code&scope=', scope, ] if state: url_list.extend(['&state=', state]) url_list.extend([ '&component_appid=', self.component.component_appid, ]) url_list.append('#wechat_redirect') return ''.join(url_list) def fetch_access_token(self, code): """获取 access_token :param code: 授权完成跳转回来后 URL 中的 code 参数 :return: JSON 数据包 """ res = self._get( 'sns/oauth2/component/access_token', params={ 'appid': self.app_id, 'component_appid': self.component.component_appid, 'component_access_token': self.component.access_token, 'code': code, 'grant_type': 'authorization_code', } ) self.access_token = res['access_token'] self.open_id = res['openid'] self.refresh_token = res['refresh_token'] self.expires_in = res['expires_in'] self.scope = res['scope'] return res def refresh_access_token(self, refresh_token): """刷新 access token :param refresh_token: OAuth2 refresh token :return: JSON 数据包 """ res = self._get( 'sns/oauth2/component/refresh_token', params={ 'appid': self.app_id, 'grant_type': 'refresh_token', 'refresh_token': refresh_token, 'component_appid': self.component.component_appid, 'component_access_token': self.component.access_token, } ) self.access_token = res['access_token'] self.open_id = res['openid'] self.refresh_token = res['refresh_token'] self.expires_in = res['expires_in'] self.scope = res['scope'] return res def get_user_info(self, openid=None, access_token=None, lang='zh_CN'): """ 获取用户基本信息(需授权作用域为snsapi_userinfo) 如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。 :param openid: 可选,微信 openid,默认获取当前授权用户信息 :param access_token: 可选,access_token,默认使用当前授权用户的 access_token :param lang: 可选,语言偏好, 默认为 ``zh_CN`` :return: JSON 数据包 """ openid = openid or self.open_id access_token = access_token or self.access_token return self._get( 'sns/userinfo', params={ 'access_token': access_token, 'openid': openid, 'lang': lang } ) def _request(self, method, url_or_endpoint, **kwargs): if not url_or_endpoint.startswith(('http://', 'https://')): url = '{base}{endpoint}'.format( base=self.API_BASE_URL, endpoint=url_or_endpoint ) else: url = url_or_endpoint if isinstance(kwargs.get('data', ''), dict): body = json.dumps(kwargs['data'], ensure_ascii=False) body = body.encode('utf-8') kwargs['data'] = body res = self._http.request( method=method, url=url, **kwargs ) try: res.raise_for_status() except requests.RequestException as reqe: raise WeChatOAuthException( errcode=None, errmsg=None, client=self, request=reqe.request, response=reqe.response ) return self._handle_result(res, method=method, url=url, **kwargs) def _handle_result(self, res, method=None, url=None, **kwargs): result = json.loads(res.content.decode('utf-8', 'ignore'), strict=False) if 'errcode' in result: result['errcode'] = int(result['errcode']) if 'errcode' in result and result['errcode'] != 0: errcode = result['errcode'] errmsg = result.get('errmsg', errcode) if self.component.auto_retry and errcode in ( WeChatErrorCode.INVALID_CREDENTIAL.value, WeChatErrorCode.INVALID_ACCESS_TOKEN.value, WeChatErrorCode.EXPIRED_ACCESS_TOKEN.value): logger.info('Component access token expired, fetch a new one and retry request') self.component.fetch_access_token() kwargs['params']['component_access_token'] = self.component.access_token return self._request( method=method, url_or_endpoint=url, **kwargs ) elif errcode == WeChatErrorCode.OUT_OF_API_FREQ_LIMIT.value: # api freq out of limit raise APILimitedException( errcode, errmsg, client=self, request=res.request, response=res ) else: raise WeChatComponentOAuthException( errcode, errmsg, client=self, request=res.request, response=res ) return result def _get(self, url, **kwargs): return self._request( method='get', url_or_endpoint=url, **kwargs )