重复 强烈建议看 官网文档 微信有很多开放接口,那么必然需要权限验证,access_token 和 jsapi_ticket是我们使用微信高级接口必不可少的数据,而且它们都是有有效期的。
一、access_token (我厚颜无耻的复制了官方文档)
access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
公众平台的API调用所需的access_token的使用及生成方式说明:
1、建议公众号开发者使用中控服务器统一获取和刷新access_token,其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突,导致access_token覆盖而影响业务;
2、目前access_token的有效期通过返回的expire_in来传达,目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。在刷新过程中,中控服务器可对外继续输出的老access_token,此时公众平台后台会保证在5分钟内,新老access_token都可用,这保证了第三方业务的平滑过渡;
3、access_token的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新access_token的接口,这样便于业务服务器在API调用获知access_token已超时的情况下,可以触发access_token的刷新流程。
公众号和小程序均可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在“微信公众平台-开发-基本配置”页中获得(需要已经成为开发者,且帐号没有异常状态)。**调用接口时,请登录“微信公众平台-开发-基本配置”提前将服务器IP地址添加到IP白名单中,点击查看设置方法,否则将无法调用成功。**小程序无需配置IP白名单。
二、jsapi_ticket
生成js-sdk签名之前必须使用jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。
由此可见重新生成token之后,前一个token就无法使用,如果我们不统一管理,很多模块之间会冲突,因此我们需要一个控制模块,定时对这两个数据进行刷新,以及保存,统一分发
三、上代码(完整代码 见 这篇文章)
参考网上的机制 是使用Redis数据库进行存取
# 缓存 access_token 和 jsapi_ticket,并且即使更新,防止过期
# access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。
# 开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时
# jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取
# 我们用redis去存数据,并设置过期时间
import redis
from wxlogger import logger
class BaseCache(object):
"""缓存类父类"""
_host = '127.0.0.1'
_port = 6379
_database = 0
_password = ''
@property
def redis_ctl(self):
"""redis控制句柄,就是连接对象"""
redis_ctl = redis.Redis(host=self._host, port=self._port, db=self._database, password=self._password)
return redis_ctl
class TokenCache(BaseCache):
"""微信token缓存"""
_expire_access_token = 7200 # 微信access_token过期时间, 2小时
_expire_js_token = 7200 # 微信jsapi_ticket, 过期时间, 7200秒
def set_access_cache(self, key, value):
"""添加微信access_token验证相关redis"""
self.redis_ctl.set(key, value)
# 设置过期时间
self.redis_ctl.expire(key, self._expire_access_token)
logger.info('更新了 access_token')
def set_js_cache(self, key, value):
"""添加网页授权相关redis"""
self.redis_ctl.set(key, value)
# 设置过期时间
self.redis_ctl.expire(key, self._expire_js_token)
logger.info('更新了 js_token')
def get_cache(self, key):
"""获取redis"""
try:
v = (self.redis_ctl.get(key)).decode('utf-8')
return v
except Exception as e:
logger.error('wxcache'+str(e))
return None
然后我们启动 tornado自带的定时函数 tornado.ioloop.PeriodicCallback(callback, callback_time, io_loop=None),将他放在tornado程序中运行
import tornado.ioloop
import requests
import json
from wxconfig import WxConfig
from wxcache import TokenCache
from wxlogger import logger
class WxShedule(object):
"""负责access_token和jsapi_ticket的更新"""
_token_cache = TokenCache() # 微信token缓存实例
_expire_time_access_token = 7000 * 1000 # token过期时间
def excute(self):
"""执行定时器任务"""
# IOLoop.instance().call_later(delay, callback, *args, **kwargs)
# 延时delay秒之后,将callback加入到tornado的加入到的处理队列里面,异步调用只调用一次
tornado.ioloop.IOLoop.instance().call_later(0, self.get_access_token)
# tornado.ioloop.PeriodicCallback(callback, callback_time, io_loop=None)
# callback设定定时调用的方法 callback_time设定每次调用之间的间隔,单位毫秒
tornado.ioloop.PeriodicCallback(self.get_access_token, self._expire_time_access_token).start()
def get_access_token(self):
"""获取微信全局唯一票据access_token"""
try:
url = WxConfig.get_access_token_url
r = requests.get(url)
if r.status_code == 200:
d = json.loads(r.text)
if 'access_token' in d.keys():
access_token = d['access_token']
# 添加至redis中
self._token_cache.set_access_cache('access_token', access_token)
# 获取JS_SDK权限签名的jsapi_ticket
self.get_jsapi_ticket()
else:
errcode = d['errcode']
# 出现错误10s之后调用一次,获取access_token
tornado.ioloop.IOLoop.instance().call_later(10, self.get_access_token)
else:
# 网络错误10s之后调用一次,获取access_token
tornado.ioloop.IOLoop.instance().call_later(10, self.get_access_token)
except Exception as e:
logger.error('wxtoken get_access_token'+str(e))
def get_jsapi_ticket(self):
"""获取JS_SDK权限签名的jsapi_ticket"""
try:
# 从redis中获取access_token
access_token = self._token_cache.get_cache('access_token')
if access_token:
url = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi' % access_token
r = requests.get(url)
if r.status_code == 200:
d = json.loads(r.text)
errcode = d['errcode']
if errcode == 0:
jsapi_ticket = d['ticket']
# 添加至redis中
self._token_cache.set_js_cache('jsapi_ticket', jsapi_ticket)
else:
tornado.ioloop.IOLoop.instance().call_later(10, self.get_jsapi_ticket)
else:
# 网络错误 重新获取
tornado.ioloop.IOLoop.instance().call_later(10, self.get_jsapi_ticket)
else:
# access_token已经过期 重新获取
tornado.ioloop.IOLoop.instance().call_later(10, self.get_access_token)
except Exception as e:
logger.error('wxtoken get_jsapi_ticket'+str(e))
OK,解决了access_token 那么我们就可以用它来创建自定义菜单啦,如果为了测试,你可以写个简单的函数 单独执行一下,临时生成 token数据