【愚公系列】2022年01月 Django商城项目13-登录界面-QQ登录功能实现
【摘要】 前言 1.QQ互联开发者申请若想实现QQ登录,需要成为QQ互联的开发者,审核通过才可实现相关连接:https://connect.qq.com/第一步:首先使用qq登录第二步:注册个人应用注册成功后如下 2.QQ互联应用申请成为QQ互联开发者后,还需创建应用,即获取本项目对应与QQ互联的应用ID。相关连接:https://connect.qq.com/manage.html#/appcre...
前言
1.QQ互联开发者申请
若想实现QQ登录,需要成为QQ互联的开发者,审核通过才可实现
第一步:首先使用qq登录
第二步:注册个人应用
注册成功后如下
2.QQ互联应用申请
成为QQ互联开发者后,还需创建应用,即获取本项目对应与QQ互联的应用ID。
相关连接:https://connect.qq.com/manage.html#/appcreate/web
3.网站对接QQ登录
QQ互联提供有开发文档,帮助开发者实现QQ登录。
相关连接:http://wiki.connect.qq.com/%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C_oauth2-0
一、django实际对接流程
1.创建抽象模型类
from django.db import models
class BaseModel(models.Model):
"""为模型类补充字段"""
create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
abstract = True # 说明是抽象模型类, 用于继承使用,数据库迁移时不会创建BaseModel的表
2.创建QQ用户模型类
from django.db import models
from utils.models import BaseModel
class OAuthQQUser(BaseModel):
"""QQ登录用户数据"""
# ForeignKey 我们使用了 其他子应用的模型
# 我们采用 '子应用名.模型类名'
user = models.ForeignKey('users.User', on_delete=models.CASCADE, verbose_name='用户')
openid = models.CharField(max_length=64, verbose_name='openid', db_index=True)
class Meta:
db_table = 'tb_oauth_qq'
verbose_name = 'QQ登录用户数据'
verbose_name_plural = verbose_name
3.注册应用
# Application references
# https://docs.djangoproject.com/en/2.1/ref/settings/#std:setting-INSTALLED_APPS
INSTALLED_APPS = [
'app',
# Add your apps here to enable them
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app.users',
'app.oauth',
]
4.配置QQ登录信息
# QQ登陆相关的
QQ_CLIENT_ID = '填写自己的'
QQ_CLIENT_SECRET = '填写自己的'
QQ_REDIRECT_URI = '填写自己的'
5.登录返回的URL地址和回调信息
from django.shortcuts import render
from QQLoginTool.QQtool import OAuthQQ
from DJ_MeiDuo import settings
from django import http
from django.views import View
# Create your views here.
class OauthQQURLView(View):
def get(self,request):
#1.创建实例对象
state = 'test'
qqoauth = OAuthQQ(
client_secret=settings.QQ_CLIENT_SECRET,
client_id=settings.QQ_CLIENT_ID,
redirect_uri=settings.QQ_REDIRECT_URI,
state=state
)
#2.调用方法
login_url = qqoauth.get_qq_url()
return http.JsonResponse({'login_url':login_url})
class OauthQQUserView(View):
def get(self,request):
# 1.获取code
code = request.GET.get('code')
if code is None:
return render(request,'oauth_callback.html',context={'errmsg':'没有获取到指定参数'})
# 2. 通过读取文档将code转换为token
qqoauth = OAuthQQ(
client_secret=settings.QQ_CLIENT_SECRET,
client_id=settings.QQ_CLIENT_ID,
redirect_uri=settings.QQ_REDIRECT_URI
)
token = qqoauth.get_access_token(code)
#3.通过token换取openid
openid = qqoauth.get_open_id(token)
# 4. 我们需要根据 openid 进行数据的查询
try:
qquser = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
# 如果没有同样的openid,则说明用户没有绑定过
# 对openid进行一个加密的处理
openid_access_token = generate_access_token(openid)
return render(request,'oauth_callback.html',context={'openid_access_token':openid_access_token})
else:
# 如果有同样的openid,则说明用户绑定过
# 则直接登陆
response = redirect(reverse('contents:index'))
#1. 设置登陆状态
login(request,qquser.user)
#2.设置cookie信息
response.set_cookie('username',qquser.user.username,max_age=14*24*3600)
return response
# return render(request,'oauth_callback.html')
def post(self,request):
# 1.先接收数据
data = request.POST
# 2.获取数据
mobile = data.get('mobile')
password = data.get('pwd')
sms_code = data.get('sms_code')
access_token = data.get('access_token')
# 3.验证数据
# 省略
# 4.access_token( 加密之后的openid)解密
openid = check_access_token(access_token)
# 5.根据手机号进行用户信息的判断
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 http.HttpResponseBadRequest('密码错误')
# 则和用户进行绑定,
qquser = OAuthQQUser.objects.create(
user=user,
openid=openid
)
# 6.设置登陆的状态
login(request,user)
# 7.设置cookie信息
response = redirect(reverse('contents:index'))
response.set_cookie('username',user.username,max_age=14*24*3600)
# 8.跳转指定页面
return response
加解密方法的封装
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from app.oauth.constants import OPENID_TOKEN_EXPIRE_TIME
from DJ_MeiDuo import settings
def generate_access_token(openid):
#1. 创建实例对象
s = Serializer(secret_key=settings.SECRET_KEY,expires_in=OPENID_TOKEN_EXPIRE_TIME)
#2.组织数据
data = {
'openid':openid
}
#3.加密处理
token = s.dumps(data)
#4. 返回
return token.decode()
def check_access_token(token):
#1.创建实例对象
s = Serializer(secret_key=settings.SECRET_KEY, expires_in=OPENID_TOKEN_EXPIRE_TIME)
#2. 解密数据
result = s.loads(token)
# script = {'openid':'xxxx'}
#3.返回数据
return result['openid']
6.回调页面逻辑功能实现
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>小徐商城-绑定用户</title>
<link rel="stylesheet" type="text/css" href="{{ static('css/reset.css') }}" />
<link rel="stylesheet" type="text/css" href="{{ static('css/main.css') }}" />
<script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script>
<script src="https://cdn.staticfile.org/axios/0.18.0/axios.min.js"></script>
</head>
<body>
<div id="app" v-cloak>
<div>
<div class="register_con">
<div class="l_con fl">
<a href="index.html" class="reg_logo"><img src="{{ static('images/logo.png') }}"></a>
<div class="reg_slogan">商品美 · 种类多 · 欢迎光临</div>
<div class="reg_banner"></div>
</div>
<div class="r_con fr">
<div class="reg_title clearfix">
<h1>绑定用户</h1>
</div>
<div class="reg_form clearfix">
<form id="reg_form" method="post" @submit="on_submit">
{{ csrf_input }}
<input type="hidden" name="access_token" value="{{ openid_access_token }}">
<ul>
<li>
<label>手机号:</label>
<input type="text" name="mobile" id="phone" @blur="check_mobile" v-model="mobile">
<span class="error_tip" v-show="error_mobile">[[error_mobile_message]]</span>
</li>
<li>
<label>密码:</label>
<input type="password" name="pwd" id="pwd" @blur="check_password" v-model="password">
<span class="error_tip" v-show="error_password">请输入8-20位密码</span>
</li>
<li>
<label>图形验证码:</label>
<input type="text" name="pic_code" id="pic_code" class="msg_input" v-model="image_code">
<img :src="image_code_url" @click="generate_image_code" alt="图形验证码" class="pic_code">
<span class="error_tip" v-show="error_image_code">请填写图形验证码</span>
</li>
<li>
<label>短信验证码:</label>
<input type="text" name="sms_code" id="msg_code" class="msg_input" v-model="sms_code" @blur="check_sms_code">
<a href="javascript:;" class="get_msg_code" @click="send_sms_code">获取短信验证码</a>
<span class="error_tip" v-show="error_sms_code">请填写短信验证码</span>
</li>
<li class="reg_sub">
<input type="submit" value="保 存" name="">
</li>
</ul>
</form>
</div>
</div>
</div>
<div class="footer no-mp">
<div class="foot_link">
<a href="#">关于我们</a>
<span>|</span>
<a href="#">联系我们</a>
<span>|</span>
<a href="#">招聘人才</a>
<span>|</span>
<a href="#">友情链接</a>
</div>
<p>CopyRight © 2022 福建小徐网络科技有限公司 All Rights Reserved</p>
<p>电话:13960699696 闽ICP备*******8号</p>
</div>
</div>
</div>
<script type="text/javascript" src="{{ static('js/host.js') }}"></script>
<script type="text/javascript" src="{{ static('js/common.js') }}"></script>
<script type="text/javascript" src="{{ static('js/oauth_callback.js') }}"></script>
</body>
</html>
let vm = new Vue({
el: '#app',
delimiters: ['[[', ']]'],
data: {
mobile: '',
password: '',
image_code: '',
sms_code: '',
error_mobile: false,
error_password: false,
error_image_code: false,
error_sms_code: false,
error_mobile_message: '',
error_image_code_message: '',
error_sms_code_message: '',
error_password_message:'',
uuid: '',
image_code_url: '',
sms_code_tip: '获取短信验证码',
sending_flag: false,
},
mounted(){
// 界面获取图形验证码
this.generate_image_code();
},
methods: {
// 生成图形验证码的请求地址
generate_image_code(){
// 生成一个编号 : 严格一点的使用uuid保证编号唯一, 不是很严谨的情况下,也可以使用时间戳
this.uuid = generateUUID();
// 设置页面中图形验证码img标签的src属性
this.image_code_url = "/image_codes/" + this.uuid + "/";
},
// 检查手机号
check_mobile(){
let re = /^1[3-9]\d{9}$/;
if(re.test(this.mobile)) {
this.error_mobile = false;
} else {
this.error_mobile_message = '您输入的手机号格式不正确';
this.error_mobile = true;
}
},
// 检查密码
check_password(){
let re = /^[0-9A-Za-z]{8,20}$/;
if (re.test(this.password)) {
this.error_password = false;
} else {
this.error_password = true;
}
},
// 检查图片验证码
check_image_code(){
if(!this.image_code) {
this.error_image_code_message = '请填写图片验证码';
this.error_image_code = true;
} else {
this.error_image_code = false;
}
},
// 检查短信验证码
check_sms_code(){
if(!this.sms_code){
this.error_sms_code_message = '请填写短信验证码';
this.error_sms_code = true;
} else {
this.error_sms_code = false;
}
},
// 发送手机短信验证码
send_sms_code(){
if (this.sending_flag == true) {
return;
}
this.sending_flag = true;
// 校验参数,保证输入框有数据填写
this.check_mobile();
this.check_image_code();
if (this.error_mobile == true || this.error_image_code == true) {
this.sending_flag = false;
return;
}
// 向后端接口发送请求,让后端发送短信验证码
let url = '/sms_codes/' + this.mobile + '/?image_code=' + this.image_code+'&image_code_id='+ this.uuid;
axios.get(url, {
responseType: 'json'
})
.then(response => {
// 表示后端发送短信成功
if (response.data.code == '0') {
// 倒计时60秒,60秒后允许用户再次点击发送短信验证码的按钮
let num = 60;
// 设置一个计时器
let t = setInterval(() => {
if (num == 1) {
// 如果计时器到最后, 清除计时器对象
clearInterval(t);
// 将点击获取验证码的按钮展示的文本回复成原始文本
this.sms_code_tip = '获取短信验证码';
// 将点击按钮的onclick事件函数恢复回去
this.sending_flag = false;
} else {
num -= 1;
// 展示倒计时信息
this.sms_code_tip = num + '秒';
}
}, 1000, 60)
} else {
if (response.data.code == '4001') {
this.error_image_code_message = response.data.errmsg;
this.error_image_code = true;
} else { // 4002
this.error_sms_code_message = response.data.errmsg;
this.error_sms_code = true;
}
this.generate_image_code();
this.sending_flag = false;
}
})
.catch(error => {
console.log(error.response);
this.sending_flag = false;
})
},
// 绑定openid
on_submit(){
this.check_mobile();
this.check_password();
this.check_sms_code();
if(this.error_mobile == true || this.error_password == true || this.error_sms_code == true) {
// 不满足条件:禁用表单
window.event.returnValue = false
}
}
}
});
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)