時(shí)間:2023-05-30 21:00:01 | 來(lái)源:網(wǎng)站運(yùn)營(yíng)
時(shí)間:2023-05-30 21:00:01 來(lái)源:網(wǎng)站運(yùn)營(yíng)
Python+Tornado開(kāi)發(fā)微信公眾號(hào):本教程針對(duì)的是已掌握Python語(yǔ)言基本用法并且掌握其任一Web框架的用戶。pip install tornado
2. 選擇一款開(kāi)發(fā)Python的IDEimport loggingfrom logging import Loggerfrom logging.handlers import TimedRotatingFileHandler'''日志管理類(lèi)'''def init_logger(logger_name): if logger_name not in Logger.manager.loggerDict: logger1 = logging.getLogger(logger_name) logger1.setLevel(logging.INFO) # 設(shè)置最低級(jí)別 # logger1.setLevel(logging.DEBUG) # 設(shè)置最低級(jí)別 df = '%Y-%m-%d %H:%M:%S' format_str = '[%(asctime)s]: %(name)s %(levelname)s %(lineno)s %(message)s' formatter = logging.Formatter(format_str, df) # handler all try: handler1 = TimedRotatingFileHandler('/usr/web_wx/log/all.log', when='D', interval=1, backupCount=7) except Exception: handler1 = TimedRotatingFileHandler('F:/program/web_wx/core/log//all.log', when='D', interval=1, backupCount=7) handler1.setFormatter(formatter) handler1.setLevel(logging.DEBUG) logger1.addHandler(handler1) # handler error try: handler2 = TimedRotatingFileHandler('/usr/web_wx/log/error.log', when='D', interval=1, backupCount=7) except Exception: handler2 = TimedRotatingFileHandler('F:/program/web_wx/core/log/error.log', when='D', interval=1, backupCount=7) handler2.setFormatter(formatter) handler2.setLevel(logging.ERROR) logger1.addHandler(handler2) # console console = logging.StreamHandler() console.setLevel(logging.DEBUG) # 設(shè)置日志打印格式 console.setFormatter(formatter) # 將定義好的console日志handler添加到root logger logger1.addHandler(console) logger1 = logging.getLogger(logger_name) return logger1logger = init_logger('runtime-log')if __name__ == '__main__': logger.debug('test-debug') logger.info('test-info') logger.warn('test-warn') logger.error('test-error')
from core.logger_helper import loggerimport hashlibimport tornado.webclass WxSignatureHandler(tornado.web.RequestHandler): """ 微信服務(wù)器簽名驗(yàn)證, 消息回復(fù) check_signature: 校驗(yàn)signature是否正確 """ def data_received(self, chunk): pass def get(self): try: signature = self.get_argument('signature') timestamp = self.get_argument('timestamp') nonce = self.get_argument('nonce') echostr = self.get_argument('echostr') logger.debug('微信sign校驗(yàn),signature='+signature+',×tamp='+timestamp+'&nonce='+nonce+'&echostr='+echostr) result = self.check_signature(signature, timestamp, nonce) if result: logger.debug('微信sign校驗(yàn),返回echostr='+echostr) self.write(echostr) else: logger.error('微信sign校驗(yàn),---校驗(yàn)失敗') except Exception as e: logger.error('微信sign校驗(yàn),---Exception' + str(e)) def check_signature(self, signature, timestamp, nonce): """校驗(yàn)token是否正確""" token = 'test12345' L = [timestamp, nonce, token] L.sort() s = L[0] + L[1] + L[2] sha1 = hashlib.sha1(s.encode('utf-8')).hexdigest() logger.debug('sha1=' + sha1 + '&signature=' + signature) return sha1 == signature
from core.server.wxauthorize import WxSignatureHandlerimport tornado.web'''web解析規(guī)則'''urlpatterns = [ (r'/wxsignature', WxSignatureHandler), # 微信簽名 ]
import osimport tornado.httpserverimport tornado.ioloopimport tornado.webfrom tornado.options import define, optionsfrom core.url import urlpatternsdefine('port', default=8000, help='run on the given port', type=int)class Application(tornado.web.Application): def __init__(self): settings = dict( template_path=os.path.join(os.path.dirname(__file__), "core/template"), static_path=os.path.join(os.path.dirname(__file__), "core/static"), debug=True, login_url='/login', cookie_secret='MuG7xxacQdGPR7Svny1OfY6AymHPb0H/t02+I8rIHHE=', ) super(Application, self).__init__(urlpatterns, **settings)def main(): tornado.options.parse_command_line() http_server = tornado.httpserver.HTTPServer(Application()) http_server.listen(options.port) tornado.ioloop.IOLoop.current().start()if __name__ == '__main__': main()
def post(self): body = self.request.body logger.debug('微信消息回復(fù)中心】收到用戶消息' + str(body.decode('utf-8'))) data = ET.fromstring(body) ToUserName = data.find('ToUserName').text FromUserName = data.find('FromUserName').text MsgType = data.find('MsgType').text if MsgType == 'event': '''接收事件推送''' try: Event = data.find('Event').text if Event == 'subscribe': # 關(guān)注事件 CreateTime = int(time.time()) reply_content = '歡迎關(guān)注我的公眾號(hào)~' out = self.reply_text(FromUserName, ToUserName, CreateTime, reply_content) self.write(out) except: passdef reply_text(self, FromUserName, ToUserName, CreateTime, Content): """回復(fù)文本消息模板""" textTpl = """<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[%s]]></MsgType> <Content><![CDATA[%s]]></Content></xml>""" out = textTpl % (FromUserName, ToUserName, CreateTime, 'text', Content) return out
2. 自動(dòng)回復(fù)def post(self): body = self.request.body logger.debug('微信消息回復(fù)中心】收到用戶消息' + str(body.decode('utf-8'))) data = ET.fromstring(body) ToUserName = data.find('ToUserName').text FromUserName = data.find('FromUserName').text MsgType = data.find('MsgType').text if MsgType == 'text' or MsgType == 'voice': '''文本消息 or 語(yǔ)音消息''' try: MsgId = data.find("MsgId").text if MsgType == 'text': Content = data.find('Content').text # 文本消息內(nèi)容 elif MsgType == 'voice': Content = data.find('Recognition').text # 語(yǔ)音識(shí)別結(jié)果,UTF8編碼 if Content == u'你好': reply_content = '您好,請(qǐng)問(wèn)有什么可以幫助您的嗎?' else: # 查找不到關(guān)鍵字,默認(rèn)回復(fù) reply_content = "客服小兒智商不夠用啦~" if reply_content: CreateTime = int(time.time()) out = self.reply_text(FromUserName, ToUserName, CreateTime, reply_content) self.write(out) except: pass elif MsgType == 'event': '''接收事件推送''' try: Event = data.find('Event').text if Event == 'subscribe': # 關(guān)注事件 CreateTime = int(time.time()) reply_content = self.sys_order_reply out = self.reply_text(FromUserName, ToUserName, CreateTime, reply_content) self.write(out) except: pass def reply_text(self, FromUserName, ToUserName, CreateTime, Content): """回復(fù)文本消息模板""" textTpl = """<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[%s]]></MsgType> <Content><![CDATA[%s]]></Content></xml>""" out = textTpl % (FromUserName, ToUserName, CreateTime, 'text', Content) return out
import redis"""緩存服務(wù)器"""CACHE_SERVER = { 'host': '127.0.0.1', 'port': 6379, 'database': 0, 'password': '',}class BaseCache(object): """ 緩存類(lèi)父類(lèi) redis_ctl: redis控制句柄 """ _host = CACHE_SERVER.get('host', '') _port = CACHE_SERVER.get('port', '') _database = CACHE_SERVER.get('database', '') _password = CACHE_SERVER.get('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
from core.cache.basecache import BaseCachefrom core.logger_helper import loggerclass TokenCache(BaseCache): """ 微信token緩存 set_cache 添加redis get_cache 獲取redis """ _expire_access_token = 7200 # 微信access_token過(guò)期時(shí)間, 2小時(shí) _expire_js_token = 30 * 24 * 3600 # 微信js網(wǎng)頁(yè)授權(quán)過(guò)期時(shí)間, 30天 KEY_ACCESS_TOKEN = 'access_token' # 微信全局唯一票據(jù)access_token KEY_JSAPI_TICKET = 'jsapi_ticket' # JS_SDK權(quán)限簽名的jsapi_ticket def set_access_cache(self, key, value): """添加微信access_token驗(yàn)證相關(guān)redis""" res = self.redis_ctl.set(key, value) self.redis_ctl.expire(key, self._expire_access_token) logger.debug('【微信token緩存】setCache>>>key[' + key + '],value[' + value + ']') return res def set_js_cache(self, key, value): """添加網(wǎng)頁(yè)授權(quán)相關(guān)redis""" res = self.redis_ctl.set(key, value) self.redis_ctl.expire(key, self._expire_js_token) logger.debug('【微信token緩存】setCache>>>key[' + key + '],value[' + value + ']') return res def get_cache(self, key): """獲取redis""" try: v = (self.redis_ctl.get(key)).decode('utf-8') logger.debug(v) logger.debug('【微信token緩存】getCache>>>key[' + key + '],value[' + v + ']') return v except Exception: return None
4. 使用tornado的 Ioloop 實(shí)現(xiàn)定時(shí)獲取access_token和 jsapi_ticket,并將獲取到的access_token和 jsapi_ticket保存在Redis數(shù)據(jù)庫(kù)中class WxConfig(object): """ 微信開(kāi)發(fā)--基礎(chǔ)配置 """ AppID = 'wxxxxxxxxxxxxxxxx' # AppID(應(yīng)用ID) AppSecret = '024a7fcxxxxxxxxxxxxxxxxxxxx' # AppSecret(應(yīng)用密鑰) '''獲取access_token''' config_get_access_token_url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s' % (AppID, AppSecret)
from core.logger_helper import loggerimport tornado.ioloopimport requestsimport jsonfrom core.server.wxconfig import WxConfigfrom core.cache.tokencache import TokenCacheclass WxShedule(object): """ 定時(shí)任務(wù)調(diào)度器 excute 執(zhí)行定時(shí)器任務(wù) get_access_token 獲取微信全局唯一票據(jù)access_token get_jsapi_ticket 獲取JS_SDK權(quán)限簽名的jsapi_ticket """ _token_cache = TokenCache() # 微信token緩存實(shí)例 _expire_time_access_token = 7000 * 1000 # token過(guò)期時(shí)間 def excute(self): """執(zhí)行定時(shí)器任務(wù)""" logger.info('【獲取微信全局唯一票據(jù)access_token】>>>執(zhí)行定時(shí)器任務(wù)') tornado.ioloop.IOLoop.instance().call_later(0, self.get_access_token) tornado.ioloop.PeriodicCallback(self.get_access_token, self._expire_time_access_token).start() # tornado.ioloop.IOLoop.current().start() def get_access_token(self): """獲取微信全局唯一票據(jù)access_token""" url = WxConfig.config_get_access_token_url r = requests.get(url) logger.info('【獲取微信全局唯一票據(jù)access_token】Response[' + str(r.status_code) + ']') if r.status_code == 200: res = r.text logger.info('【獲取微信全局唯一票據(jù)access_token】>>>' + res) d = json.loads(res) if 'access_token' in d.keys(): access_token = d['access_token'] # 添加至redis中 self._token_cache.set_access_cache(self._token_cache.KEY_ACCESS_TOKEN, access_token) # 獲取JS_SDK權(quán)限簽名的jsapi_ticket self.get_jsapi_ticket() return access_token elif 'errcode' in d.keys(): errcode = d['errcode'] logger.info( '【獲取微信全局唯一票據(jù)access_token-SDK】errcode[' + errcode + '] , will retry get_access_token() method after 10s') tornado.ioloop.IOLoop.instance().call_later(10, self.get_access_token) else: logger.error('【獲取微信全局唯一票據(jù)access_token】request access_token error, will retry get_access_token() method after 10s') tornado.ioloop.IOLoop.instance().call_later(10, self.get_access_token) def get_jsapi_ticket(self): """獲取JS_SDK權(quán)限簽名的jsapi_ticket""" access_token = self._token_cache.get_cache(self._token_cache.KEY_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) logger.info('【微信JS-SDK】獲取JS_SDK權(quán)限簽名的jsapi_ticket的Response[' + str(r.status_code) + ']') if r.status_code == 200: res = r.text logger.info('【微信JS-SDK】獲取JS_SDK權(quán)限簽名的jsapi_ticket>>>>' + res) d = json.loads(res) errcode = d['errcode'] if errcode == 0: jsapi_ticket = d['ticket'] # 添加至redis中 self._token_cache.set_access_cache(self._token_cache.KEY_JSAPI_TICKET, jsapi_ticket) else: logger.info('【微信JS-SDK】獲取JS_SDK權(quán)限簽名的jsapi_ticket>>>>errcode[' + errcode + ']') logger.info('【微信JS-SDK】request jsapi_ticket error, will retry get_jsapi_ticket() method after 10s') tornado.ioloop.IOLoop.instance().call_later(10, self.get_jsapi_ticket) else: logger.info('【微信JS-SDK】request jsapi_ticket error, will retry get_jsapi_ticket() method after 10s') tornado.ioloop.IOLoop.instance().call_later(10, self.get_jsapi_ticket) else: logger.error('【微信JS-SDK】獲取JS_SDK權(quán)限簽名的jsapi_ticket時(shí),access_token獲取失敗, will retry get_access_token() method after 10s') tornado.ioloop.IOLoop.instance().call_later(10, self.get_access_token)if __name__ == '__main__': wx_shedule = WxShedule() """執(zhí)行定時(shí)器""" wx_shedule.excute()
import osimport tornado.httpserverimport tornado.ioloopimport tornado.webfrom tornado.options import define, optionsfrom core.url import urlpatternsfrom core.server.wxshedule import WxSheduledefine('port', default=8000, help='run on the given port', type=int)class Application(tornado.web.Application): def __init__(self): settings = dict( template_path=os.path.join(os.path.dirname(__file__), "core/template"), static_path=os.path.join(os.path.dirname(__file__), "core/static"), debug=True, login_url='/login', cookie_secret='MuG7xxacQdGPR7Svny1OfY6AymHPb0H/t02+I8rIHHE=', ) super(Application, self).__init__(urlpatterns, **settings)def main(): tornado.options.parse_command_line() http_server = tornado.httpserver.HTTPServer(Application()) http_server.listen(options.port) # 執(zhí)行定時(shí)任務(wù) wx_shedule = WxShedule() wx_shedule.excute() tornado.ioloop.IOLoop.current().start()if __name__ == '__main__': main()
class WxConfig(object): """ 微信開(kāi)發(fā)--基礎(chǔ)配置 """ AppID = 'wxxxxxxxxxxxxxxxx' # AppID(應(yīng)用ID) AppSecret = '024a7fcxxxxxxxxxxxxxxxxxxxx' # AppSecret(應(yīng)用密鑰) """微信網(wǎng)頁(yè)開(kāi)發(fā)域名""" AppHost = 'http://xxxxxx.com' '''獲取access_token''' config_get_access_token_url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s' % (AppID, AppSecret) '''自定義菜單創(chuàng)建接口''' menu_create_url = 'https://api.weixin.qq.com/cgi-bin/menu/create?access_token=' '''自定義菜單查詢接口''' menu_get_url = 'https://api.weixin.qq.com/cgi-bin/menu/get?access_token=' '''自定義菜單刪除接口''' menu_delete_url = 'https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=' '''微信公眾號(hào)菜單映射數(shù)據(jù)''' """重定向后會(huì)帶上state參數(shù),開(kāi)發(fā)者可以填寫(xiě)a-zA-Z0-9的參數(shù)值,最多128字節(jié)""" wx_menu_state_map = { 'menuIndex0': '%s/page/index' % AppHost, # 測(cè)試菜單1 }
class WxAuthorServer(object): """ 微信網(wǎng)頁(yè)授權(quán)server get_code_url 獲取code的url get_auth_access_token 通過(guò)code換取網(wǎng)頁(yè)授權(quán)access_token refresh_token 刷新access_token check_auth 檢驗(yàn)授權(quán)憑證(access_token)是否有效 get_userinfo 拉取用戶信息 """ """授權(quán)后重定向的回調(diào)鏈接地址,請(qǐng)使用urlencode對(duì)鏈接進(jìn)行處理""" REDIRECT_URI = '%s/wx/wxauthor' % WxConfig.AppHost """ 應(yīng)用授權(quán)作用域 snsapi_base (不彈出授權(quán)頁(yè)面,直接跳轉(zhuǎn),只能獲取用戶openid) snsapi_userinfo (彈出授權(quán)頁(yè)面,可通過(guò)openid拿到昵稱(chēng)、性別、所在地。并且,即使在未關(guān)注的情況下,只要用戶授權(quán),也能獲取其信息) """ SCOPE = 'snsapi_base' # SCOPE = 'snsapi_userinfo' """通過(guò)code換取網(wǎng)頁(yè)授權(quán)access_token""" get_access_token_url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' """拉取用戶信息""" get_userinfo_url = 'https://api.weixin.qq.com/sns/userinfo?' def get_code_url(self, state): """獲取code的url""" dict = {'redirect_uri': self.REDIRECT_URI} redirect_uri = urllib.parse.urlencode(dict) author_get_code_url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&%s&response_type=code&scope=%s&state=%s#wechat_redirect' % (WxConfig.AppID, redirect_uri, self.SCOPE, state) logger.debug('【微信網(wǎng)頁(yè)授權(quán)】獲取網(wǎng)頁(yè)授權(quán)的code的url>>>>' + author_get_code_url) return author_get_code_url def get_auth_access_token(self, code): """通過(guò)code換取網(wǎng)頁(yè)授權(quán)access_token""" url = self.get_access_token_url + 'appid=%s&secret=%s&code=%s&grant_type=authorization_code' % (WxConfig.AppID, WxConfig.AppSecret, code) r = requests.get(url) logger.debug('【微信網(wǎng)頁(yè)授權(quán)】通過(guò)code換取網(wǎng)頁(yè)授權(quán)access_token的Response[' + str(r.status_code) + ']') if r.status_code == 200: res = r.text logger.debug('【微信網(wǎng)頁(yè)授權(quán)】通過(guò)code換取網(wǎng)頁(yè)授權(quán)access_token>>>>' + res) json_res = json.loads(res) if 'access_token' in json_res.keys(): return json_res elif 'errcode' in json_res.keys(): errcode = json_res['errcode']
import requestsimport jsonfrom core.server.wxconfig import WxConfigfrom core.cache.tokencache import TokenCachefrom core.logger_helper import loggerfrom core.server.wxauthorize import WxAuthorServerclass WxMenuServer(object): """ 微信自定義菜單 create_menu 自定義菜單創(chuàng)建接口 get_menu 自定義菜單查詢接口 delete_menu 自定義菜單刪除接口 create_menu_data 創(chuàng)建菜單數(shù)據(jù) """ _token_cache = TokenCache() # 微信token緩存 _wx_author_server = WxAuthorServer() # 微信網(wǎng)頁(yè)授權(quán)server def create_menu(self): """自定義菜單創(chuàng)建接口""" access_token = self._token_cache.get_cache(self._token_cache.KEY_ACCESS_TOKEN) if access_token: url = WxConfig.menu_create_url + access_token data = self.create_menu_data() r = requests.post(url, data.encode('utf-8')) logger.debug('【微信自定義菜單】自定義菜單創(chuàng)建接口Response[' + str(r.status_code) + ']') if r.status_code == 200: res = r.text logger.debug('【微信自定義菜單】自定義菜單創(chuàng)建接口' + res) json_res = json.loads(res) if 'errcode' in json_res.keys(): errcode = json_res['errcode'] return errcode else: logger.error('【微信自定義菜單】自定義菜單創(chuàng)建接口獲取不到access_token') def get_menu(self): """自定義菜單查詢接口""" access_token = self._token_cache.get_cache(self._token_cache.KEY_ACCESS_TOKEN) if access_token: url = WxConfig.menu_get_url + access_token r = requests.get(url) logger.debug('【微信自定義菜單】自定義菜單查詢接口Response[' + str(r.status_code) + ']') if r.status_code == 200: res = r.text logger.debug('【微信自定義菜單】自定義菜單查詢接口' + res) json_res = json.loads(res) if 'errcode' in json_res.keys(): errcode = json_res['errcode'] return errcode else: logger.error('【微信自定義菜單】自定義菜單查詢接口獲取不到access_token') def delete_menu(self): """自定義菜單刪除接口""" access_token = self._token_cache.get_cache(self._token_cache.KEY_ACCESS_TOKEN) if access_token: url = WxConfig.menu_delete_url + access_token r = requests.get(url) logger.debug('【微信自定義菜單】自定義菜單刪除接口Response[' + str(r.status_code) + ']') if r.status_code == 200: res = r.text logger.debug('【微信自定義菜單】自定義菜單刪除接口' + res) json_res = json.loads(res) if 'errcode' in json_res.keys(): errcode = json_res['errcode'] return errcode else: logger.error('【微信自定義菜單】自定義菜單刪除接口獲取不到access_token') def create_menu_data(self): """創(chuàng)建菜單數(shù)據(jù)""" menu_data = {'button': []} # 大菜單 menu_Index0 = { 'type': 'view', 'name': '測(cè)試菜單1', 'url': self._wx_author_server.get_code_url('menuIndex0') } menu_data['button'].append(menu_Index0) MENU_DATA = json.dumps(menu_data, ensure_ascii=False) logger.debug('【微信自定義菜單】創(chuàng)建菜單數(shù)據(jù)MENU_DATA[' + str(MENU_DATA) + ']') return MENU_DATAif __name__ == '__main__': wx_menu_server = WxMenuServer() '''創(chuàng)建菜單數(shù)據(jù)''' # wx_menu_server.create_menu_data() # '''自定義菜單創(chuàng)建接口''' wx_menu_server.create_menu() '''自定義菜單查詢接口''' # wx_menu_server.get_menu() '''自定義菜單刪除接口''' # wx_menu_server.delete_menu()
wx_handler.pyimport tornado.webfrom core.logger_helper import loggerfrom core.server.wxauthorize import WxConfigfrom core.server.wxauthorize import WxAuthorServerfrom core.cache.tokencache import TokenCacheclass WxHandler(tornado.web.RequestHandler): """ 微信handler處理類(lèi) """ '''微信配置文件''' wx_config = WxConfig() '''微信網(wǎng)頁(yè)授權(quán)server''' wx_author_server = WxAuthorServer() '''redis服務(wù)''' wx_token_cache = TokenCache() def post(self, flag): if flag == 'wxauthor': '''微信網(wǎng)頁(yè)授權(quán)''' code = self.get_argument('code') state = self.get_argument('state') # 獲取重定向的url redirect_url = self.wx_config.wx_menu_state_map[state] logger.debug('【微信網(wǎng)頁(yè)授權(quán)】將要重定向的地址為:redirct_url[' + redirect_url + ']') logger.debug('【微信網(wǎng)頁(yè)授權(quán)】用戶同意授權(quán),獲取code>>>>code[' + code + ']state[' + state + ']') if code: # 通過(guò)code換取網(wǎng)頁(yè)授權(quán)access_token data = self.wx_author_server.get_auth_access_token(code) openid = data['openid'] logger.debug('【微信網(wǎng)頁(yè)授權(quán)】openid>>>>openid[' + openid + ']') if openid: # 跳到自己的業(yè)務(wù)界面 self.redirect(redirect_url) else: # 獲取不到openid logger.debug('獲取不到openid')
六. 菜單中網(wǎng)頁(yè)的開(kāi)發(fā), JS-SDK的使用import timeimport randomimport stringimport hashlibfrom core.server.weixin.wxconfig import WxConfigfrom core.server.cache.tokencache import TokenCachefrom core.logger_helper import loggerclass WxSign: """/ 微信開(kāi)發(fā)--獲取JS-SDK權(quán)限簽名 __create_nonce_str 隨機(jī)字符串 __create_timestamp 時(shí)間戳 sign 生成JS-SDK使用權(quán)限簽名 """ def __init__(self, jsapi_ticket, url): self.ret = { 'nonceStr': self.__create_nonce_str(), 'jsapi_ticket': jsapi_ticket, 'timestamp': self.__create_timestamp(), 'url': url } def __create_nonce_str(self): return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(15)) def __create_timestamp(self): return int(time.time()) def sign(self): string = '&'.join(['%s=%s' % (key.lower(), self.ret[key]) for key in sorted(self.ret)]) self.ret['signature'] = hashlib.sha1(string.encode('utf-8')).hexdigest() logger.debug('【微信JS-SDK】獲取JS-SDK權(quán)限簽名>>>>dict[' + str(self.ret) + ']') return self.retif __name__ == '__main__': token_cache = TokenCache() jsapi_ticket = token_cache.get_cache(token_cache.KEY_JSAPI_TICKET) # 注意 URL 一定要?jiǎng)討B(tài)獲取,不能 hardcode url = '%s/order/index' % WxConfig.AppHost sign = WxSign(jsapi_ticket, url) print(sign.sign())
$ git clone git://github.com/yyuu/pyenv.git ~/.pyenv
(2) 配置$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc$ echo 'eval "$(pyenv init -)"' >> ~/.bashrc
(3) 重新加載shell$ exec $SHELL -l
2.通過(guò)pyenv安裝多個(gè)版本的pythonyum install readline readline-devel readline-static -yyum install openssl openssl-devel openssl-static -yyum install sqlite-devel -yyum install bzip2-devel bzip2-libs -y
(2) 安裝需要的python版本pyenv install 3.5.0然后刷新python版本pyenv rehash
(3) 設(shè)置全局Python版本$ pyenv global 3.5.0
3.使用虛擬環(huán)境virtualenv$ pip install virtualenv
(2) 使用方法$ virtualenv --no-site-packages wx_env
(b)用source進(jìn)入該環(huán)境$ source wx_env/bin/activate
注意到命令提示符變了,有個(gè)(venv)前綴,表示當(dāng)前環(huán)境是一個(gè)名為venv的Python環(huán)境pip freeze > ./requirements.txtpip install -r requirements.txt
5.安裝redis服務(wù)make make install
(6) 修改配置文件vi /etc/redis/redis.conf
僅修改: daemonize yes (no-->yes)/usr/local/bin/redis-server /usr/local/redis/redis.conf
(8) 查看啟動(dòng)ps -ef | grep redis
6.安裝nginxwget http://nginx.org/download/nginx-1.10.0.tar.gz
(2) 解壓Nginx的tar包,并進(jìn)入解壓好的目錄tar -zxvf nginx-1.10.0.tar.gzcd nginx-1.10.0/
(3) 安裝zlib和pcre庫(kù)yum -y install zlib zlib-develyum -y install pcre pcre-devel
(4) 配置、編譯并安裝./configure
makemake install
(5) 啟動(dòng)nginx/usr/local/nginx/sbin/nginx
訪問(wèn)服務(wù)器后如下圖顯示說(shuō)明Nginx運(yùn)正常。user nobody;worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024;}http { include mime.types; default_type application/octet-stream; upstream web_wx { server 127.0.0.1:8001; server 127.0.0.1:8002; server 127.0.0.1:8003; server 127.0.0.1:8004; } sendfile on; #tcp_nopush on; keepalive_timeout 65; proxy_read_timeout 200; tcp_nopush on; tcp_nodelay on; gzip on; gzip_min_length 1000; gzip_proxied any; server { listen 80; server_name localhost; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } location / { proxy_pass_header Server; proxy_set_header Host $http_host; # proxy_redirect false; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_pass http://web_wx; } }}
8.配置Supervisord yum install supervisor
(2) 設(shè)置開(kāi)機(jī)自啟wget -O /etc/rc.d/init.d/supervisord https://gist.githubusercontent.com/gracece/21e5719b234929799eeb/raw/supervisord
(3)將守護(hù)進(jìn)程添加為服務(wù)chmod +x /etc/rc.d/init.d/supervisordchkconfig --add supervisord #加為服務(wù)ntsysv #運(yùn)行ntsysv,選中supervisord啟動(dòng)系統(tǒng)時(shí)跟著啟動(dòng)。
(4) 設(shè)置 /etc/supervisord.conf文件[unix_http_server]file=/tmp/supervisor.sock ; (the path to the socket file);chmod=0700 ; socket file mode (default 0700);chown=nobody:nogroup ; socket file uid:gid owner;username=user ; (default is no username (open server));password=123 ; (default is no password (open server));[inet_http_server] ; inet (TCP) server disabled by default;port=127.0.0.1:9001 ; (ip_address:port specifier, *:port for all iface);username=user ; (default is no username (open server));password=123 ; (default is no password (open server))[supervisord]logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB)logfile_backups=10 ; (num of main logfile rotation backups;default 10)loglevel=info ; (log level;default info; others: debug,warn,trace)pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)nodaemon=false ; (start in foreground if true;default false)minfds=1024 ; (min. avail startup file descriptors;default 1024)minprocs=200 ; (min. avail process descriptors;default 200);umask=022 ; (process file creation umask;default 022);user=chrism ; (default is current user, required if root);identifier=supervisor ; (supervisord identifier, default is 'supervisor');directory=/tmp ; (default is not to cd during start);nocleanup=true ; (don't clean up tempfiles at start;default false);childlogdir=/tmp ; ('AUTO' child log dir, default $TEMP);environment=KEY="value" ; (key value pairs to add to environment);strip_ansi=false ; (strip ansi escape codes in logs; def. false)[rpcinterface:supervisor]supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface[supervisorctl]serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket;username=grace ; should be same as http_username if set;password=grace ; should be same as http_password if set;prompt=mysupervisor ; cmd line prompt (default "supervisor");history_file=~/.sc_history ; use readline history if available[group:tornadoApp]programs=web_wx[program:web_wx]command=python /var/web_wx/run.py --port=80%(process_num)02ddirectory=/var/web_wx/process_name = %(program_name)s%(process_num)dautorestart=trueredirect_stderr=truestdout_logfile=/var/log/tornado.logstdout_logfile_maxbytes=500MBstdout_logfile_backups=50stderr_logfile=/var/log/tornado-error.logloglevel=infonumprocs = 4numprocs_start = 1
(5) 開(kāi)啟守護(hù)進(jìn)程服務(wù)supervisordsupervisorctl reload allsupervisorctl status
★ 關(guān)于項(xiàng)目源碼關(guān)鍵詞:公眾
客戶&案例
營(yíng)銷(xiāo)資訊
關(guān)于我們
客戶&案例
營(yíng)銷(xiāo)資訊
關(guān)于我們
微信公眾號(hào)
版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。