|
@@ -0,0 +1,182 @@
|
|
|
+from __future__ import unicode_literals
|
|
|
+
|
|
|
+import json
|
|
|
+import requests
|
|
|
+
|
|
|
+__all__ = ("wx_login")
|
|
|
+
|
|
|
+try:
|
|
|
+ unicode = unicode
|
|
|
+except NameError:
|
|
|
+ # python 3
|
|
|
+ basestring = (str, bytes)
|
|
|
+else:
|
|
|
+ # python 2
|
|
|
+ bytes = str
|
|
|
+
|
|
|
+
|
|
|
+class WeixinError(Exception):
|
|
|
+
|
|
|
+ def __init__(self, msg):
|
|
|
+ super(WeixinError, self).__init__(msg)
|
|
|
+
|
|
|
+
|
|
|
+class Map(dict):
|
|
|
+ """
|
|
|
+ 提供字典的dot访问模式
|
|
|
+ Example:
|
|
|
+ m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
|
|
|
+ """
|
|
|
+ def __init__(self, *args, **kwargs):
|
|
|
+ super(Map, self).__init__(*args, **kwargs)
|
|
|
+ for arg in args:
|
|
|
+ if isinstance(arg, dict):
|
|
|
+ for k, v in arg.items():
|
|
|
+ if isinstance(v, dict):
|
|
|
+ v = Map(v)
|
|
|
+ self[k] = v
|
|
|
+
|
|
|
+ if kwargs:
|
|
|
+ for k, v in kwargs.items():
|
|
|
+ if isinstance(v, dict):
|
|
|
+ v = Map(v)
|
|
|
+ self[k] = v
|
|
|
+
|
|
|
+ def __getattr__(self, attr):
|
|
|
+ return self[attr]
|
|
|
+
|
|
|
+ def __setattr__(self, key, value):
|
|
|
+ self.__setitem__(key, value)
|
|
|
+
|
|
|
+ def __getitem__(self, key):
|
|
|
+ if key not in self.__dict__:
|
|
|
+ super(Map, self).__setitem__(key, {})
|
|
|
+ self.__dict__.update({key: Map()})
|
|
|
+ return self.__dict__[key]
|
|
|
+
|
|
|
+ def __setitem__(self, key, value):
|
|
|
+ super(Map, self).__setitem__(key, value)
|
|
|
+ self.__dict__.update({key: value})
|
|
|
+
|
|
|
+ def __delattr__(self, item):
|
|
|
+ self.__delitem__(item)
|
|
|
+
|
|
|
+ def __delitem__(self, key):
|
|
|
+ super(Map, self).__delitem__(key)
|
|
|
+ del self.__dict__[key]
|
|
|
+
|
|
|
+
|
|
|
+class WeixinLoginError(WeixinError):
|
|
|
+
|
|
|
+ def __init__(self, msg):
|
|
|
+ super(WeixinLoginError, self).__init__(msg)
|
|
|
+
|
|
|
+
|
|
|
+class WeixinLogin(object):
|
|
|
+
|
|
|
+ app_id = ""
|
|
|
+ app_secret = ""
|
|
|
+
|
|
|
+ def init_app(self, app, prefix=""):
|
|
|
+ self.app_id = app.config.get(prefix + "WXMP_APP_ID")
|
|
|
+ self.app_secret = app.config.get(prefix + "WXMP_APP_SECRET")
|
|
|
+
|
|
|
+ def _get(self, url, params):
|
|
|
+ resp = requests.get(url, params=params)
|
|
|
+ data = Map(json.loads(resp.content.decode("utf-8")))
|
|
|
+ if data.errcode:
|
|
|
+ msg = "%(errcode)d %(errmsg)s" % data
|
|
|
+ raise WeixinLoginError(msg)
|
|
|
+ return data
|
|
|
+
|
|
|
+ def authorize(self, redirect_uri, scope="snsapi_base", state=None):
|
|
|
+ """
|
|
|
+ 生成微信认证地址并且跳转
|
|
|
+
|
|
|
+ :param redirect_uri: 跳转地址
|
|
|
+ :param scope: 微信认证方式,有`snsapi_base`跟`snsapi_userinfo`两种
|
|
|
+ :param state: 认证成功后会原样带上此字段
|
|
|
+ """
|
|
|
+ assert scope in ["snsapi_base", "snsapi_userinfo", "snsapi_login"]
|
|
|
+ if scope == "snsapi_login":
|
|
|
+ url = "https://open.weixin.qq.com/connect/qrconnect"
|
|
|
+ else:
|
|
|
+ url = "https://open.weixin.qq.com/connect/oauth2/authorize"
|
|
|
+ data = dict()
|
|
|
+ data.setdefault("appid", self.app_id)
|
|
|
+ data.setdefault("redirect_uri", redirect_uri)
|
|
|
+ data.setdefault("response_type", "code")
|
|
|
+ data.setdefault("scope", scope)
|
|
|
+ if state:
|
|
|
+ data.setdefault("state", state)
|
|
|
+ data = [(k, data[k]) for k in sorted(data.keys()) if data[k]]
|
|
|
+ s = "&".join("=".join(kv) for kv in data if kv[1])
|
|
|
+ print(s)
|
|
|
+ return "{0}?{1}#wechat_redirect".format(url, s)
|
|
|
+
|
|
|
+ def access_token(self, code):
|
|
|
+ """
|
|
|
+ 获取令牌
|
|
|
+ """
|
|
|
+ url = "https://api.weixin.qq.com/sns/oauth2/access_token"
|
|
|
+ args = dict()
|
|
|
+ args.setdefault("appid", self.app_id)
|
|
|
+ args.setdefault("secret", self.app_secret)
|
|
|
+ args.setdefault("code", code)
|
|
|
+ args.setdefault("grant_type", "authorization_code")
|
|
|
+
|
|
|
+ return self._get(url, args)
|
|
|
+
|
|
|
+ def auth(self, access_token, openid):
|
|
|
+ """
|
|
|
+ 检验授权凭证
|
|
|
+
|
|
|
+ :param access_token: 授权凭证
|
|
|
+ :param openid: 唯一id
|
|
|
+ """
|
|
|
+ url = "https://api.weixin.qq.com/sns/auth"
|
|
|
+ args = dict()
|
|
|
+ args.setdefault("access_token", access_token)
|
|
|
+ args.setdefault("openid", openid)
|
|
|
+
|
|
|
+ return self._get(url, args)
|
|
|
+
|
|
|
+ def refresh_token(self, refresh_token):
|
|
|
+ """
|
|
|
+ 重新获取access_token
|
|
|
+
|
|
|
+ :param refresh_token: 刷新令牌
|
|
|
+ """
|
|
|
+ url = "https://api.weixin.qq.com/sns/oauth2/refresh_token"
|
|
|
+ args = dict()
|
|
|
+ args.setdefault("appid", self.app_id)
|
|
|
+ args.setdefault("grant_type", "refresh_token")
|
|
|
+ args.setdefault("refresh_token", refresh_token)
|
|
|
+
|
|
|
+ return self._get(url, args)
|
|
|
+
|
|
|
+ def userinfo(self, access_token, openid):
|
|
|
+ """
|
|
|
+ 获取用户信息
|
|
|
+
|
|
|
+ :param access_token: 令牌
|
|
|
+ :param openid: 用户id,每个应用内唯一
|
|
|
+ """
|
|
|
+ url = "https://api.weixin.qq.com/sns/userinfo"
|
|
|
+ args = dict()
|
|
|
+ args.setdefault("access_token", access_token)
|
|
|
+ args.setdefault("openid", openid)
|
|
|
+ args.setdefault("lang", "zh_CN")
|
|
|
+
|
|
|
+ return self._get(url, args)
|
|
|
+
|
|
|
+ def user_info(self, access_token, openid):
|
|
|
+ """
|
|
|
+ 获取用户信息
|
|
|
+
|
|
|
+ 兼容老版本0.3.0,与WeixinMP的user_info冲突
|
|
|
+ """
|
|
|
+ return self.userinfo(access_token, openid)
|
|
|
+
|
|
|
+
|
|
|
+wx_login = WeixinLogin()
|