既然后用到了QQ登录,那么,我们就免不了需要查看一下QQ登录的开发文档了。
实现QQ登录时,要对接QQ互联,而QQ互联提供有开发文档,帮助开发者实现QQ登录
- 相关链接:http://wiki.connect.qq.com/%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C_oauth2-0
通过开发文档我们了解到QQ使用的是OAuth2.0认证,这里简单的介绍一下OAuth2.0认证。
OAuth: OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容。
QQ登录OAuth2.0:对于用户相关的OpenAPI(例如获取用户信息,动态同步,照片,日志,分享等),为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向用户征求授权。
QQ登录OAuth2.0采用OAuth2.0标准协议来进行用户身份验证和获取用户授权,相对于之前的OAuth1.0协议,其认证流程更简单和安全。
通过开发文档我们还了解到QQ登录实现的流程,接下来我将为大家简单的介绍一下QQ登录的开发流程。
QQ登录开发的三大步骤如下:
1. 获取Authorization Code
2. 使用Authorization Code 获取 Access Token
3. 使用Access Token 获取openid
这里我要介绍一款第三方个工具包QQLoginTool:
QQLoginTool该工具封装了QQ登录时对接QQ互联接口的请求和响应操作,可用于快速实现OAuth2.0认证。
首先,我们需要在Django的虚拟环境中安装这个模块。
pip install QQLoginTool
然后,我们就需要给用户一个QQ登陆页面,然后接受当用户使用QQ登陆成功后返回的Authorization Code。
# 1. 导入模块
from QQLoginTool.QQtool import OAuthQQ
from django.conf import settings
# 2. 实例对象
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET, redirect_uri=settings.QQ_REDIRECT_URI, state=next)
'''
# 配置QQ登陆数据
QQ_CLIENT_ID = 'appid'
QQ_CLIENT_SECRET = 'appkey'
QQ_REDIRECT_URI = '回调地址'
'''
# 3. 获得登陆页面
login_url = oauth.get_qq_url()
# 4.返回登录页面
return http.JsonResponse({'code':RETCODE.OK,'errmsg':'OK','login_url':login_url})
# 5. 获取Authorization Code
authorization_code = request.GET.get('code')
当我们获取到Authorization Code后,我们拿着这个code,再次向QQ服务器发送请求,获取Access Token。
# 通过authorization_code获取 Access Token
access_token = oauth.get_access_token(authorization_code)
当我们获取到Access Token后,我们要再次向QQ服务器发送请求,获取openid
# 通过access_token 获取 openid
openid = oauth.get_open_id(access_token)
当我们成功的拿到openid后,就说明我们的OAutho2.0认证通过了。
那么接下来就是对openid的操作了,这就需要看你们的业务经理的需求了,业务经理需要是什么我们就拿着这个openid去获取什么,如果业务经理需要将openid保存,那么我们就会将openid保存起来即可。
美多商城项目中,使用openid和美多商城的用户进行绑定。
其逻辑是:
判断openid是否已经绑定了用户,如果绑定用户了,就直接登录;
如果没有则绑定用户。
代码实现为:
class QQAuthURLView(View):
def get(self, request):
next = request.GET.get('next', '/')
# 2. 创建OAuthQQ对象
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET, redirect_uri=settings.QQ_REDIRECT_URI, state=next)
# 3. 获取QQ登录页面,并返回
login_url = oauth.get_qq_url()
return http.JsonResponse({'code':RETCODE.OK,'errmsg':'OK','login_url':login_url})
class QQAuthUserView(View):
def get(self, request):
# 4. 通过QQ登陆成功的页面,获取返回的Authorization Code
authorization_code = request.GET.get('code')
# 判断authorization_code 是否存在
if not authorization_code:
return http.HttpResponseForbidden('缺少必传参数')
# 创建工具对象
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET,
redirect_uri=settings.QQ_REDIRECT_URI)
try:
# 5. 通过authorization_code获取 Access Token
access_token = oauth.get_access_token(authorization_code)
# 6. 通过access_token 获取 openid
openid = oauth.get_open_id(access_token)
except Exception as e:
logging.error(e)
return http.HttpResponseServerError('0Auth2.0认证失败')
try:
# 使用openid查询该QQ用户是否绑定过美多商城用户
oauth_user = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
# 如果openid没绑定美多商城用户,响应绑定页面,并返回openid
# 使用itsdangerous对openid进行加密处理
access_token_openid = generate_access_token_openid(openid)
# 讲加密后的openid返回给前端
context = {'access_token_openid':access_token_openid}
return render(request,'oauth_callback.html', context)
else:
# 如果openid已绑定美多商城用户,直接登入到美多商城系统
# 实现状态保持
qq_user = oauth_user.user
login(request, qq_user)
# 重定向到登录前的页面
next = request.GET.get('state')
response = redirect(next)
# 返回前段页面用户名
response.set_cookie('username',qq_user.username,max_age=3600 * 24 * 14)
return response
def post(self, request):
'''接受绑定页面数据'''
# 接收参数
mobile = request.POST.get('mobile')
password = request.POST.get('password')
sms_code_client = request.POST.get('sms_code')
access_token_openid = request.POST.get('openid')
# 校验参数
# 判断参数是否齐全
if not all([mobile, password, sms_code_client, access_token_openid]):
return http.HttpResponseForbidden('缺少必传参数')
# 判断手机号是否合法
if not re.match(r'^1[3-9]\d{9}$', mobile):
return http.HttpResponseForbidden('请输入正确的手机号码')
# 判断密码是否合格
if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
return http.HttpResponseForbidden('请输入8-20位的密码')
# 判断短信验证码是否一致
redis_conn = get_redis_connection('verify_code')
sms_code_server = redis_conn.get('sms_%s' % mobile)
if sms_code_server is None:
return render(request, 'oauth_callback.html', {'sms_code_errmsg': '无效的短信验证码'})
if sms_code_client != sms_code_server.decode():
return render(request, 'oauth_callback.html', {'sms_code_errmsg': '输入短信验证码有误'})
# 获取openid明文
openid = check_access_token_openid(access_token_openid)
# 判断openid是否有效,因为openid有效期为600秒
if not openid:
return render(request, 'oauth_callback.html', {'openid_errmsg': '无效的openid'})
try:
# 判断绑定用户是否存在
user = User.objects.get(mobile=mobile)
except User.DoesNotExist:
# 如果不存在,则创建用户
user = User.objects.create_user(username=mobile,password=password,mobile=mobile)
else:
# 如果存在,则校验密码
if not user.check_password(password):
return render(request, 'oauth_callback.html', {'account_errmsg': '账号或密码错误'})
try:
# 将openid绑定到用户
OAuthQQUser.objects.create(openid=openid,user=user)
except Exception as e:
logger.error(e)
return render(request, 'oauth_callback.html', {'qq_login_errmsg': 'QQ登录失败'})
login(request, user)
# 重定向state==next
next = request.GET.get('state')
resposne = redirect(next)
# QQ登录后的用户名写入到cookie
resposne.set_cookie('username', user.username, max_age=3600 * 24 * 7)
# 响应结果
return resposne