Skip to content

Instantly share code, notes, and snippets.

@cloverstd
Last active November 4, 2019 21:33
Show Gist options
  • Save cloverstd/4311d15f7e4a6c50b37b to your computer and use it in GitHub Desktop.
Save cloverstd/4311d15f7e4a6c50b37b to your computer and use it in GitHub Desktop.
微信二维码扫描登录
#!/usr/bin/env python
# encoding: utf-8
import tornado.ioloop
import tornado.httpclient
import tornado.web
import tornado.gen
import json
import tornado.websocket
import Queue
import uuid
import copy
from wechatpy import parse_message, create_reply
from wechatpy.utils import check_signature
from wechatpy.exceptions import InvalidSignatureException
from wechatpy import WeChatClient
TOKEN = 'token'
app_id = 'YOUR APPID'
app_secret = 'YOUR APPSECRET'
client = WeChatClient(app_id, app_secret)
#print client.fetch_access_token()
access_token = 'F6jLHXNGMSv_OfJlYSU6ToB5SyArwaskqu01Rbt49uiCTs5xJ1UmDWGagasHYjOqkw58juL76eoGXz-d2hgPAQ1Vv_mqiBC4tv9x7dosfLY'
clients = list()
users = list()
q = Queue.LifoQueue(1000)
for i in xrange(1000):
q.put(i)
users.append(dict(username=str(i),
openid=None))
class WeChat(object):
def __init__(self, token):
self.base_api_url = 'https://api.weixin.qq.com/cgi-bin'
self.create_ticket_url = self.base_api_url + '/qrcode/create'
self.show_qrcode_url = 'https://mp.weixin.qq.com/cgi-bin/showqrcode'
self.http_client = tornado.httpclient.AsyncHTTPClient()
self.access_token = token
@tornado.gen.coroutine
def get_ticket(self, expires=1800, scene_id=1):
body = {
"expire_seconds": expires,
"action_name": "QR_LIMIT_STR_SCENE",
"action_info": {
"scene": {
"scene_str": str(scene_id)
}
}
}
url = self.create_ticket_url + '?access_token=' + self.access_token
request = tornado.httpclient.HTTPRequest(url=url,
method='POST',
body=json.dumps(body))
response = yield self.http_client.fetch(request)
response = json.loads(response.body)
raise tornado.gen.Return(response)
@tornado.gen.coroutine
def get_qr_url(self, expires=1800, scene_id=1):
data = yield self.get_ticket(expires, scene_id)
print data
url = self.show_qrcode_url + '?ticket=' + data['ticket']
raise tornado.gen.Return(url)
class WebSocketHandler(tornado.websocket.WebSocketHandler):
@tornado.gen.coroutine
def open(self):
print 'open'
wechat = WeChat(access_token)
qid = q.get()
qr = yield wechat.get_ticket(180, qid)
c = {
'client': self,
'qid': qid,
'ticket': qr['ticket'],
'uuid': str(uuid.uuid4()),
'expires': 180,
}
clients.append(c)
d = copy.copy(c)
del d['qid'], d['client']
self.write_message(json.dumps(dict(type='qr', data=d)))
@tornado.gen.coroutine
def on_message(self, message):
msg = None
try:
msg = json.loads(message)
except:
msg = message
pass
print msg
def on_close(self):
for c in clients:
if c['client'] == self:
q.put(c['qid'])
clients.remove(c)
print 'WebSocket closed'
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
def post(self):
username = self.get_argument('username')
for user in users:
if user['username'] == username:
print 'ok'
self.set_cookie('user', username)
return self.redirect('/user', 302)
self.redirect('/', 302)
class UserCenterHandler(tornado.web.RequestHandler):
def get_current_user(self):
user = self.get_cookie('user')
for _user in users:
if _user['username'] == user:
return _user
return user
@tornado.web.authenticated
def get(self):
self.render('usercenter.html', user=self.current_user)
class UserBindHandler(tornado.web.RequestHandler):
def post(self):
print self.request.body
print self.request.arguments
username = self.get_argument('username')
openid = self.get_argument('openid')
for user in users:
if user['username'] == username:
user['openid'] = openid
data = {
'status': True
}
self.set_cookie('user', username)
break
else:
data = {
'status': False,
'err': '用户不存在'
}
self.set_status(403)
self.set_header('content-type', 'application/json')
for user in users:
if user['openid']:
print user
self.write(json.dumps(data))
class LogoutHandler(tornado.web.RequestHandler):
def get(self):
self.clear_all_cookies()
self.redirect('/', 302)
class WeChatHandler(tornado.web.RequestHandler):
def get(self):
signature = self.get_argument('signature', '')
timestamp = self.get_argument('timestamp', '')
nonce = self.get_argument('nonce', '')
echo_str = self.get_argument('echostr', '')
try:
check_signature(TOKEN, signature, timestamp, nonce)
except InvalidSignatureException:
self.set_status(403)
return self.write('hi')
self.write(echo_str)
def post(self):
signature = self.get_argument('signature', '')
timestamp = self.get_argument('timestamp', '')
nonce = self.get_argument('nonce', '')
try:
check_signature(TOKEN, signature, timestamp, nonce)
except InvalidSignatureException:
self.set_status('403')
return self.write('hi')
msg = parse_message(self.request.body)
print msg
if msg.type == 'text':
reply = create_reply(msg.content, msg)
elif msg.type == 'event':
if msg.event == 'scan':
reply = create_reply('扫描', msg)
for c in clients:
if c['qid'] == int(msg.scene_id):
for user in users:
if user['openid'] == msg.source:
c['client'].write_message(dict(type='success', data=user['username']))
break
else:
c['client'].write_message(dict(type='success_and_not_bind', data=msg.source))
elif msg.event == 'subscribe_scan':
reply = create_reply('扫描关注', msg)
for c in clients:
if c['qid'] == int(msg.scene_id):
for user in users:
if user['openid'] == msg.source:
c['client'].write_message(dict(type='success', data=user['username']))
break
else:
c['client'].write_message(dict(type='success_and_not_bind', data=msg.source))
else:
reply = create_reply('Sorry, can not handle this for now', msg)
self.write(reply.render())
application = tornado.web.Application([
(r'/', IndexHandler),
(r'/user', UserCenterHandler),
(r'/user/bind', UserBindHandler),
(r'/user/logout', LogoutHandler),
(r'/ws', WebSocketHandler),
(r'/wechat', WeChatHandler),
],
template_path='templates',
login_url="/",
debug=True,
cookie_secret='key',
)
if __name__ == '__main__':
application.listen(5000)
tornado.ioloop.IOLoop.instance().start()
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="well">
<form class="form-horizontal" method="POST">
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">Username</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputEmail3" placeholder="Username" name="username">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Sign in</button>
</div>
</div>
</form>
</div>
</div>
<div class="col-md-3">
<img class="img-thumbnail" src="" id="wechat-qr-img" alt="">
</div>
</div>
<div class="row" id="wechat-bind" style="display:none;">
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="well">
<form class="form-horizontal">
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">Username</label>
<div class="col-sm-10">
<input type="text" class="form-control" placeholder="Username" name="username" id="bind-username">
<input type="hidden" class="form-control" placeholder="" name="" id="bind-openid">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="button" class="btn btn-default" id="wechat-bind-btn">Bind</button>
</div>
</div>
</form>
</div>
</div>
<div class="col-md-3"></div>
</div>
</div>
{% end %}
{% block script %}
<script>
var ws = new WebSocket('ws://' + window.location.host + '/ws');
$(function() {
ws.onopen = function() {
console.info('open');
var get_qr = {
type: 'get_qr'
}
ws.send(JSON.stringify(get_qr));
};
var t;
ws.onmessage = function (evt) {
var msg = JSON.parse(evt.data);
console.log(msg);
if (msg.type == 'qr') {
$('#wechat-qr-img').attr('src', 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=' + msg.data.ticket)
var expires = msg.data.expires;
function run() {
console.info(expires);
expires--;
if (expires == 0) {
clearTimeout(t);
$('#wechat-qr-img').attr('src', '');
alert('二维码过期');
window.location.reload();
}
t = setTimeout(function(){run()}, 1000);
}
run();
} else if (msg.type == 'success_and_not_bind') {
clearTimeout(t);
$('#wechat-bind').slideDown();
$('#bind-openid').val(msg.data);
} else if (msg.type == 'success') {
$.cookie('user', msg.data);
window.location.href = '/user';
}
};
$('#wechat-bind-btn').click(function() {
var username = $('#bind-username').val();
var openid = $('#bind-openid').val();
console.info(username);
console.info(openid);
$.post('/user/bind', {openid: openid, username: username})
.success(function(data) {
console.log(data);
if (data.status) {
window.location.href = '/user';
}
})
.fail(function(data) {
console.error(data.responseJSON);
});
});
});
</script>
{% end %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>Bootstrap 101 Template</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="http://cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="http://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
{% block content %}{% end %}
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script src="http://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
{% block script %}
{% end %}
</body>
</html>
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="well">
当前用户:{{ user }}
<a class="btn btn-danger" href="/user/logout">Sign Out</a>
</div>
</div>
<div class="col-md-3"></div>
</div>
</div>
{% end %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment