公司最近做了个论坛使用django开发的,其中用户登陆部分打算升级为微信扫码登陆,调查了一些资料终于实现,现把实现方法贴出来大家一起学习下。微信现在接口现在越来越严格了,每出点新功能都要各种验证,而且接口调用还不固定,现在就一家独大程序员只能各种忍了。
这次开发没有采用微信推荐的OAuth2.0协议方式实现微信扫码登陆,OAuth2.0协议要求比较多,首先你必须是服务号,你的账号需要注册并通过微信开放平台认证,有一个已审核通过的网站应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。
微信开放平台认证的认证非常麻烦,而且已经认证的微信公账号居然还要认证一次,我只想说中国程序员的工作量就是被这些大公司的不负责造成的。不使用OAuth2.0怎样实现微信扫码网页里的二维码后自动登录呢?
在网页里实现微信扫码登陆,需要准备以下:
1.一个认证好的微信公众号
2.微信扫码登陆步骤
你可能会用到微信测试公众账号
def createRandomString(len):
import random
#print ('wet'.center(10,'*'))
raw = ""
range1 = range(58, 65) # between 0~9 and A~Z
range2 = range(91, 97) # between A~Z and a~z
i = 0
while i < len:
seed = random.randint(48, 122)
if ((seed in range1) or (seed in range2)):
continue;
raw += chr(seed);
i += 1
# print(raw)
return raw
def get_access_token():
access_token = ''
try:
if cache.has_key('access_token') and cache.get('access_token') != '':
access_token = cache.get('access_token')
logging.critical('cache access_token:'+access_token)
else:
appId = APP_ID
appSecret = APP_SECRET
postUrl = ("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s" % (appId, appSecret))
logging.debug(postUrl)
urlResp = urllib.urlopen(postUrl).read()
logging.critical(urlResp)
urlResp = json.loads(urlResp)
access_token = urlResp['access_token']
cache.set('access_token', access_token, 60 * 100)
#leftTime = urlResp['expires_in']
except Exception, e:
logging.critical(e.message)
return access_token
def get_qr_ticket(login_code):
ticket = ''
try:
# if cache.has_key('ticket') and cache.get('ticket') != '':
# ticket = cache.get('ticket')
# logging.critical('cache ticket:'+ticket)
# else:
token = get_access_token()
logging.critical('get_access_token for ticket:'+token)
data = {
'expire_seconds': 604800,
'action_name' :'QR_STR_SCENE',
'action_info' : {
'scene' : {
'scene_str' : login_code
}
}}
import requests as reqs
params = json.dumps(data)
#params = urllib.parse.urlencode(data).encode(encoding='UTF8')
#headers = {'Accept-Charset': 'utf-8', 'Content-Type': 'application/json'}
if token != '' :
ticket_url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={}".format(token)
response = reqs.post(url=ticket_url, data=params)
#response = reqs.urlopen(req).read()
#get_qr_ticket = urllib.urlopen(ticket_url)
#urlResp = get_qr_ticket.read().decode("utf-8")
logging.critical(response.content)
js_ticket = json.loads(response.content)
ticket = js_ticket.get("ticket")
#cache.set('ticket', ticket, 60 * 100)
#r.setex('wx:ticket', ticket, 7200)
except Exception as e:
return ''
return ticket
def ajax_signin(request):
#cache.set('access_token', '',1)
from django.http import JsonResponse
login_code = request.GET.get('login_code', None)#User.objects.filter(openid='123qwe')
#logging.critical('login_code is :' + login_code)
msg = ''
next_url=''
if login_code:
#search unique user
try:
up = UserProfile.objects.get(login_code=login_code)
except Exception:
up = None
#logging.critical('up is :' + str(up.pk))
if up :
user =User.objects.get(pk=up.pk)
if user:
#no passwsod login
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)
msg = 'success'
login_redirect_url = getattr(django_settings, 'LOGIN_REDIRECT_URL', None)
next_url = get_next_url(request, default=login_redirect_url)
if next_url == reverse('user_signin'):
next_url = '%(next)s?next=%(next)s' % {'next': next_url}
#return HttpResponseRedirect(next_url)
name_dict = {'msg':msg,'next_url':next_url}
return JsonResponse(name_dict)
EventKey: session id,对应 步骤1中 login_code 参数信息
FromUserName:扫码的用户openid
def get_wx_userinfo(openid):
bc_data = {}
try:
token = get_access_token()
logging.critical('get_access_token for ticket:'+token)
import requests as reqs
if token != '' :
ticket_url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={}&openid={}&lang=zh_CN".format(token,openid)
response = reqs.post(url=ticket_url)
logging.critical('get_wx_userinfo:'+response.content)
bc_data = json.loads(response.content)
except Exception as e:
return ''
return bc_data
@csrf_exempt
def weixin_token(request):
try:
if request.method == 'GET' and request.GET.get('echostr') != '':
import hashlib
wechat_data = request.GET
signature = wechat_data['signature']
timestamp = wechat_data['timestamp']
nonce = wechat_data['nonce']
logging.info("handle/GET func: hashcode, signature:{0} {1}".format(signature, timestamp))
echostr = wechat_data['echostr']
#token = '35977a76fc8a3239867a67a62cf45f0d'
check_list = [APP_TOKEN, timestamp, nonce]
check_list.sort()
s1 = hashlib.sha1()
s1.update(''.join(check_list).encode())
hashcode = s1.hexdigest()
logging.debug("handle/GET func: hashcode, signature:{0} {1}".format(hashcode, signature))
if hashcode == signature:
return HttpResponse(echostr)
else:
postBody = request.body
logging.critical(postBody)
import xmltodict
dictBody = xmltodict.parse(postBody)
res = WXResponse(dictBody)
type = res.check_event()
logging.critical('type:'+type)
#scan code
if type == 'scan' or type == 'unsub_scan':
openid = res.data.get("FromUserName")
logging.critical('openid:'+openid)
if type == 'unsub_scan':
login_code = res.data.get("EventKey").replace('qrscene_','')
else:
login_code = res.data.get("EventKey")
logging.critical('login_code:'+login_code)
#search unique user
try:
up = UserProfile.objects.get(openid=openid)
except Exception:
up = None
#user =User.objects.get(pk=up.pk)
# if has user
if up :
UserProfile.objects.filter(openid=openid).update(login_code=login_code)
else :
res = get_wx_userinfo(openid)
logging.critical('nickname:'+res['nickname'])
user = User()
#keep unique username
user.username = res['nickname'] + createRandomString(4)
user.set_unusable_password()
user.first_name = res['nickname']
user.last_name = ''
user.email = login_code + '@neui.net'
user.is_staff = False
user.is_superuser = False
user.is_active = True
user.save()
#avatar_urls=res['headimgurl'],
logging.critical('user.pk:'+str(user.pk))
#upn = UserProfile.objects.filter(pk=user.pk).update(openid = openid,login_code = login_code)
#logging.critical('upn:'+str(upn))
up_obj = UserProfile.objects.get(pk=user.pk)
up_obj.openid = openid
up_obj.login_code = login_code
up_obj.save()
logging.critical('save data')
except Exception, e:
logging.critical(e.message)
return HttpResponse("")