官方文档
这是对于网页应用而言,前提是你已经在微信开发着平台注册好了应用并且设置好了回调地址
大体思路:
废话少说上代码:
# 微信浏览(微信公众号内三方网页)直接走这个会自动调取微信授权确认。确认以后回跳到配置的回调地址。
CONNECT_OAUTH = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid={}&redirect_uri={}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect'
# 网页三方登陆走这个
WEB_OAUTH = 'https://open.weixin.qq.com/connect/qrconnect?appid={}&redirect_uri={}&response_type=code&scope=snsapi_login&state=WEB_STATE#wechat_redirect'
class WeChatOauth(object):
'''
微信Oauth
'''
@classmethod
def get_user_info(cls, access_token, openid):
url = 'https://api.weixin.qq.com/sns/userinfo'
data = {
"access_token": access_token,
"openid": openid,
"lang": 'en'
}
response = requests.get(url, params=data)
response.encoding = 'utf-8'
data = response.json()
nickname = data.get('nickname', False)
unionid = data.get('unionid', False)
headimgurl = data.get('headimgurl', '')
logger.info('wechat data: ' + str(data))
return nickname, unionid, headimgurl, data
@classmethod
def get_token(cls, code, state):
url = 'https://api.weixin.qq.com/sns/oauth2/access_token'
data = {
'grant_type': 'authorization_code',
'appid': settings.WECHAT_APP_ID,#这里使用你自己的
'secret': settings.WECHAT_APP_SECRET,#这里使用你自己的
'code': code,
'state': state,
}
data_web = {
'grant_type': 'authorization_code',
'appid': settings.WECHAT_WEB_APP_ID,#这里使用你自己的
'secret': settings.WECHAT_WEB_APP_SECRET,#这里使用你自己的
'code': code,
'state': state,
}
logger.info("wechat state: " + state)
if 'WEB_STATE' == state:
response = requests.get(url=url, params=data_web)
access_token_info = response.json()
access_token = access_token_info.get('access_token', False)
openid = access_token_info.get('openid', False)
else:
response = requests.get(url=url, params=data)
access_token_info = response.json()
access_token = access_token_info.get('access_token', False)
openid = access_token_info.get('openid', False)
return access_token, openid
class OauthWechatView(generic.View):
'''
获取微信token,并根据该token获取用户信息
当本地数据库已有用户信息,则直接返回token
当未有记录,则返回信息,到下一步进行绑定
'''
def get(self, request):
next = request.GET.get("next", '/')
if not next or next == 'None':
next = '/?1=1'
enterprise_id = request.GET.get("enterprise_id", None)
if enterprise_id:
next = next + '&enterprise_id=' + enterprise_id
else:
enterprise = Enterprise.objects.filter(is_local=True).first()
if enterprise:
enterprise_id = enterprise.id
logger.info("登录跳转: " + next)
# 上面这些代码是其他功能模块需要用到的,无需理会
code = request.GET.get('code')
state = request.GET.get('state')
user_id = request.GET.get('user_id', None)
# 根据code,state 获取到access_token, openid
access_token, openid = WeChatOauth.get_token(code, state)
# 再根据access_token, openid获取到用户的个人信息,uid是用户的在微信端的唯一凭证
name, uid, headimgurl, profile = WeChatOauth.get_user_info(access_token, openid)
logger.info("wechat avatar: " + headimgurl)
user = None
if 'WEB_STATE' == state:
# 如果是web过来的,直接到首页
url = resolve_url('user:edit_profile_page')
else:
url = resolve_url('user:bind') + "?next=" + next
response = redirect(url)
# 如果没有获取到uid
if not uid:
# 如果state是扫码登录的话则再次跳转到扫码页面,否则跳转到绑定手机页面(跳转此url,如果未登录的话,还会直接跳转到绑定手机页面,等于三方登陆加后台创建用户)
if 'WEB_STATE' == state:
return redirect('user:wechat_web_oauth')
else:
callback = urllib.parse.urlencode({"key": "?next={}".format(next)}).split('=', 1)[1]
uri = CONNECT_OAUTH.format(settings.WECHAT_APP_ID, settings.WECHAT_CALLBACK + callback)
return redirect(uri)
# 确保后续能拿到
request.session['unionid'] = uid
logger.info("unionid is: " + uid)
logger.info("openid is: " + openid)
oauth_list = Oauth.objects.filter(uid=uid, type=OAUTH_TYPE_WECHAT)
if not oauth_list:
# 未生成记录时, 则创建相关记录, unionid 写入 cookie, 然后到绑定页面
username = get_unique_username()
key = username
urlencode_next = url_encode(next)
url = resolve_url('user:bind') + "?next=" + urlencode_next + "&redis_key=" + key
response = redirect(url)
# set to redis授权信息
redis.set(key, dumps([headimgurl, uid, enterprise_id, openid, OAUTH_TYPE_WECHAT, name]))
return response
else:
# 当已经存在第三方数据时,则检测是否用户是否绑定了手机,没有则返回到绑定手机页面
user = User.objects.get(id=oauth_list.first().user_id)
user_info = user.owner
if not user_info.image:
image_to_oss_task.apply_async(args=[str(user_info.id), headimgurl])
oauth = oauth_list.first()
if not oauth.openid and 'STATE' == state:
oauth.openid = openid
oauth.save()
# 如果用户手机号和用户的信息都存在的话,那么跳转到next
if user_info.tel or user_info.first:
response = redirect(next)
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
login(self.request, user)
# unionid 写入 cookie, 然后到绑定页面
response.set_cookie('jwt_token', token, max_age=api_settings.JWT_EXPIRATION_DELTA.total_seconds())
response.set_cookie('unionid', uid, expires=payload.get('exp'))
return response
# 此函数作用:绑定用户
class BindPhoneView(TemplateView):
'''
绑定手机页面
到该页面时,用户已经存在,这里只是做账号关联 或者 是否合并账号
'''
template_name = 'user/bind_phone.html'
def get_context_data(self, **kwargs):
context = super(BindPhoneView, self).get_context_data(**kwargs)
context['next'] = self.request.GET.get('next', '/')
context['user_id'] = self.request.GET.get('user_id', None)
context['redis_key'] = self.request.GET.get("redis_key")
return context
def post(self, request):
phone = request.POST.get('phone')
code = request.POST.get('code')
user_id = request.POST.get('user_id', None)
next = request.POST.get("next", '/')
# 迁移公众号,开放平台逻辑
redis_key = request.POST.get("redis_key")
logger.info("redis_key {}, {}".format(redis_key, next))
response = redirect(next)
memory_code = VerificationCode.get_code_by_phone(phone)
# shixf 判断用户是否完整输入手机号和验证码
if not all([phone,code]):
url = resolve_url('user:bind') + "?next=" + next + "&redis_key=" + redis_key
return redirect(url)
if memory_code != code:
# messages.error(request, u'验证码错误') shixf 导入messages包一直报错,无奈之举
url = resolve_url('user:bind') + "?next=" + next + "&redis_key=" + redis_key
return redirect(url)
# 迁移公众号,开放平台逻辑
stream = redis.get(redis_key)
if not stream:
# messages.error(request, u'未授权!')
url = resolve_url('user:bind') + "?next=" + next + "&redis_key=" + redis_key
return redirect(url)
headimgurl, uid, enterprise_id, openid, OAUTH_TYPE_WECHAT, name = loads(stream)
# 判断手机号是否存在,如果存在,更新对应的unionid和openid
user_info = UserInfo.objects.filter(tel=phone, type=TYPE_USER_NORMAL).first()
logger.info("user_info: {}".format(user_info))
# shixf 如果用户存在,则创建一条user_oauth三方表的记录,将微信号和已存在的手机号进行关联
if user_info:
try:
oauth = Oauth(type=OAUTH_TYPE_WECHAT, uid=uid, user=user_info.owner, openid=openid, enterprise_id=enterprise_id)
oauth.save()
user = user_info.owner
# shixf 将用户的微信头像设置为用户头像
if headimgurl:
image_to_oss(str(user_info.id), headimgurl)
except:
return HttpResponse("发生未知错误,,,")
logger.info("update old user uid {}, openid {},tel {}, name {}".format(uid, openid, phone, user_info.name))
# if user_info:
# update_data = {
# "uid": uid,
# "openid": openid
# }
# user = user_info.owner
# Oauth.objects.filter(user_id=user_info.owner_id).update(**update_data)
# logger.info("update old user uid {}, openid {},tel {}, name {}".format(uid, openid, phone, user_info.name))
else:
# 不存在,新增用户
user = User.objects.create_user(username=redis_key)
smsResponse, make_name,password=VerificationCode.send_verification_code(phone=phone,template_code=2)
user_info = UserInfo(owner=user, name=name,
user_id=str(uuid.uuid1()).replace('-', ''), passwd=make_password(str(password)),
enterprise_id=enterprise_id, tel=phone)
user_info.save()
account = create_user_account()
# 异步存储图片
# image_to_oss_task.apply_async(args=[str(user_info.id), headimgurl])
# shixf 将用户的微信头像设置为用户头像
if headimgurl:
image_to_oss(str(user_info.id), headimgurl)
user_account = UserAccount(owner=user, account=account)
user_account.save()
oauth = Oauth(type=OAUTH_TYPE_WECHAT, uid=uid, user=user, openid=openid, enterprise_id=enterprise_id)
oauth.save()
logger.info("new user info, {}".format(loads(stream)))
# 删除
redis.delete(redis_key)
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
login(self.request, user)
# unionid 写入 cookie, 然后到绑定页面
response.set_cookie('jwt_token', token, max_age=api_settings.JWT_EXPIRATION_DELTA.total_seconds())
response.set_cookie('unionid', uid, expires=payload.get('exp'))
return response
打完收工
代码里有很多其他模块的功能代码,剔除即可,放心使用