Python Tornado之搭建微信公众号token们的缓存统一(四)

        重复 强烈建议看 官网文档 微信有很多开放接口,那么必然需要权限验证,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数据

你可能感兴趣的:(Python)