美多商城项目之用户登录:账号登录QQ登录
Posted 黑马程序员官方
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了美多商城项目之用户登录:账号登录QQ登录相关的知识,希望对你有一定的参考价值。
一、账号登录
1.1 用户名登录
1. 用户名登录逻辑分析
2. 用户名登录接口设计
1.请求方式
选项 | 方案 |
---|---|
请求方法 | POST |
请求地址 | /login/ |
2.请求参数:表单
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
username | string | 是 | 用户名 |
password | string | 是 | 密码 |
remembered | string | 是 | 是否记住用户 |
3.响应结果:JSON
字段 | 说明 |
---|---|
登录失败 | 响应错误提示 |
登录成功 | 重定向到首页 |
3. 用户名登录接口定义
class LoginView(View):
"""用户名登录"""
def post(self, request):
"""
实现登录逻辑
:param request: 请求对象
:return: 登录结果
"""
pass
4. 用户名登录后端逻辑
import json
from django.contrib.auth import authenticate,login
class LoginView(View):
def post(self,request):
# 1.接收参数
dict = json.loads(request.body.decode())
username = dict.get('username')
password = dict.get('password')
remembered = dict.get('remembered')
# 2.校验(整体 + 单个)
if not all([username, password]):
return JsonResponse('code': 400,
'errmsg': '缺少必传参数')
# 3.验证是否能够登录
user = authenticate(username=username,
password=password)
# 判断是否为空,如果为空,返回
if user is None:
return JsonResponse('code': 400,
'errmsg': '用户名或者密码错误')
# 4.状态保持
login(request, user)
# 5.判断是否记住用户
if remembered != True:
# 7.如果没有记住: 关闭立刻失效
request.session.set_expiry(0)
else:
# 6.如果记住: 设置为两周有效
request.session.set_expiry(None)
# 8.返回json
return JsonResponse('code': 0,
'errmsg': 'ok')
1.2 多账号登录
- Django自带的用户认证后端默认是使用用户名实现用户认证的。
1. 根据登录数据修改用户认证字段
import re
if re.match('^1[3-9]\\d9$', username):
# 手机号
User.USERNAME_FIELD = 'mobile'
else:
# account 是用户名
# 根据用户名从数据库获取 user 对象返回.
User.USERNAME_FIELD = 'username'
1.3 首页用户名展示
1. 首页用户名展示方案
方案一
- 发送ajax请求获取用户信息
- 缺点:需要发送网络请求
<div class="login_btn fl">
# ajax渲染 #
</div>
方案二
- Vue读取cookie渲染用户信息
<div v-if="username" class="login_btn fl">
欢迎您:<em>[[ username ]]</em>
<span>|</span>
<a href="#">退出</a>
</div>
<div v-else class="login_btn fl">
<a href="login.html">登录</a>
<span>|</span>
<a href="register.html">注册</a>
</div>
结论:
- 对比此两个方案,我们在本项目中选择 方案二
实现步骤:
- 注册或登录后,用户名写入到cookie
2. 用户名写入到cookie
response = JsonResponse('code': 0,
'errmsg': 'ok')
# 注册时用户名写入到cookie,有效期15天
response.set_cookie('username', user.username, max_age=3600 * 24 * 15)
return response
1.4 退出登录
1. 接口设计和定义
1.请求方式
选项 | 方案 |
---|---|
请求方法 | DELETE |
请求地址 | /logout/ |
2.请求参数
无
3.响应结果:JSON
字段 | 说明 |
---|---|
code | 状态码 |
errmsg | 错误信息 |
2. logout()方法介绍
-
退出登录:
- 回顾登录:将通过认证的用户的唯一标识信息,写入到当前session会话中
- 退出登录:正好和登录相反(清理session会话信息)
-
logout()方法:
- Django用户认证系统提供了
logout()
方法 - 封装了清理session的操作,帮助我们快速实现登出一个用户
- Django用户认证系统提供了
-
logout()位置:
django.contrib.auth.__init__.py
文件中
logout(request)
3. logout()方法使用
from django.contrib.auth import logout
class LogoutView(View):
"""退出登录"""
def delete(self, request):
"""实现退出登录逻辑"""
# 清理session
logout(request)
# 退出登录,重定向到登录页
response = JsonResponse('code':0,
'errmsg':'ok')
# 退出登录时清除cookie中的username
response.delete_cookie('username')
return response
提示:
- 由于首页中用户名是从cookie中读取的。
- 所以退出登录时,需要将cookie中用户名清除。
1.5 判断用户是否登录
1.LoginRequiredMixin
判断用户是否登录
Django用户认证系统提供了方法
request.user.is_authenticated
来判断用户是否登录。如果通过登录验证则返回True。反之,返回False。- LoginRequiredMixin封装了判断用户是否登录的操作。
from django.contrib.auth.mixins import LoginRequiredMixin
class UserInfoView(LoginRequiredMixin, View):
"""用户中心"""
def get(self, request):
"""提供个人信息界面"""
return http.JsonResponse(
'code': 0,
'errmsg': '个人中心',
"info_data":
"username":"itcast",
"mobile": "18310820688",
"email": "",
"email_active": 'true'
)
2. 判断用户是否登录并返回JSON
重要提示:
- 只有用户登录时才能让其绑定邮箱。
- 此时前后端交互的数据类型是JSON,所以需要判断用户是否登录并返回JSON给用户。
实现方案:自定义LoginRequiredJSONMixin
扩展类
- 新建
meiduo_mall/utils/views.py
文件
from django.contrib.auth.mixins import LoginRequiredMixin
from django import http
class LoginRequiredJSONMixin(LoginRequiredMixin):
"""Verify that the current user is authenticated."""
def handle_no_permission(self):
return http.JsonResponse('code': 400, 'errmsg': '用户未登录')
handle_no_permission说明:我们只需要改写父类中的处理方式 至于如何判断用户是否登录 在父类中已经判断了
LoginRequiredJSONMixin
的使用
from meiduo_mall.utils.views import LoginRequiredJSONMixin
class UserInfoView(LoginRequiredJSONMixin, View):
"""添加邮箱"""
def put(self, request):
"""实现添加邮箱逻辑"""
# 判断用户是否登录并返回JSON
pass
二、QQ登录
2.1 QQ登录开发文档
QQ登录:即我们所说的第三方登录,是指用户可以不在本项目中输入密码,而直接通过第三方的验证,成功登录本项目。
1. QQ互联开发者申请步骤
若想实现QQ登录,需要成为QQ互联的开发者,审核通过才可实现。
- 相关连接: 成为开发者 — QQ互联WIKI
2. QQ互联应用申请步骤
成为QQ互联开发者后,还需创建应用,即获取本项目对应与QQ互联的应用ID。
- 相关连接: 创建应用 — QQ互联WIKI
3. 网站对接QQ登录步骤
QQ互联提供有开发文档,帮助开发者实现QQ登录。
- 相关连接: 准备工作_OAuth2.0 — QQ互联WIKI
4. QQ登录流程分析
2.1 定义QQ登录模型类
1. 定义模型类基类
在
meiduo_mall.utils/models.py
文件中创建模型类基类。
- 用于增加数据新建时间和更新时间。
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的表
上面代码中相关参数解读:
auto_now_add:创建或添加对象时自动添加时间, 修改或更新对象时, 不会更改时间
auto_now:凡是对对象进行操作(创建/添加/修改/更新),时间都会随之改变
abstract:声明该模型类仅继承使用,数据库迁移时不会创建 BaseModel 的表
2. 定义QQ登录模型类
创建一个新的应用
oauth
,用来实现QQ第三方认证登录。
设置路由
path('', include('apps.oauth.urls')),
from django.conf.urls import url
from . import views
urlpatterns = [
]
在
oauth/models.py
中定义QQ身份(openid)与用户模型类User的关联关系
from django.db import models
from utils.models import BaseModel
class OAuthQQUser(BaseModel):
"""QQ登录用户数据"""
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. 迁移QQ登录模型类
python manage.py makemigrations
python manage.py migrate
2.3 QQ登录工具QQLoginTool
1. QQLoginTool介绍
- 该工具封装了对接QQ互联的请求操作。
- 可用于快速实现QQ登录的一种工具包。
2. QQLoginTool安装
pip install QQLoginTool
3. QQLoginTool使用说明
1.导入
from QQLoginTool.QQtool import OAuthQQ
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登录扫码页面,扫码后得到
Authorization Code
login_url = oauth.get_qq_url()
4.通过
Authorization Code
获取Access Token
access_token = oauth.get_access_token(code)
5.通过
Access Token
获取OpenID
openid = oauth.get_open_id(access_token)
2.4 OAuth2.0认证获取openid
待处理业务逻辑
# 提取code请求参数
# 使用code向QQ服务器请求access_token
# 使用access_token向QQ服务器请求openid
# 使用openid查询该QQ用户是否在美多商城中绑定过用户
# 如果openid已绑定美多商城用户,直接生成JWT token,并返回
# 如果openid没绑定美多商城用户,创建用户并绑定到openid
1. 获取QQ登录扫码页面
1.请求方式
选项 | 方案 |
---|---|
请求方法 | GET |
请求地址 | /qq/authorization/ |
2.请求参数:查询参数
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
next | string | 否 | 用于记录QQ登录成功后进入的网址 |
3.响应结果:JSON
字段 | 说明 |
---|---|
code | 状态码 |
errmsg | 错误信息 |
login_url | QQ登录扫码页面链接 |
4.后端逻辑实现
from django import http
from django.conf import settings
from django.views import View
from QQLoginTool.QQtool import OAuthQQ
import logging
logger = logging.getLogger('django')
class QQAuthURLView(View):
"""提供QQ登录页面网址
https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=xxx&redirect_uri=xxx&state=xxx
"""
def get(self, request):
# next表示从哪个页面进入到的登录页面,将来登录成功后,就自动回到那个页面
next = request.GET.get('next')
# 获取QQ登录页面网址
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,
client_secret=settings.QQ_CLIENT_SECRET,
redirect_uri=settings.QQ_REDIRECT_URI,
state=next)
login_url = oauth.get_qq_url()
return http.JsonResponse('code': 0,
'errmsg': 'OK',
'login_url':login_url)
5.QQ登录参数
# QQ登录参数
# 我们申请的 客户端id
QQ_CLIENT_ID = '101474184'
# 我们申请的 客户端秘钥
QQ_CLIENT_SECRET = 'c6ce949e04e12ecc909ae6a8b09b637c'
# 我们申请时添加的: 登录成功后回调的路径
QQ_REDIRECT_URI = 'http://www.meiduo.site:8080/oauth_callback.html'
2. 接收Authorization Code
提示:
- 用户在QQ登录成功后,QQ会将用户重定向到我们配置的回调网址。
- 在QQ重定向到回调网址时,会传给我们一个
Authorization Code
。- 我们需要拿到
Authorization Code
并完成OAuth2.0认证获取openid。- 在本项目中,我们申请QQ登录开发资质时配置的回调网址为:
http://www.meiduo.site:8000/oauth_callback
- QQ互联重定向的完整网址为:
http://www.meiduo.site:8000/oauth_callback/?code=AE263F12675FA79185B54870D79730A7&state=%2F
path('oauth_callback/', views.QQAuthUserView.as_view()),
class QQAuthUserView(View):
"""用户扫码登录的回调处理"""
def get(self, request):
"""Oauth2.0认证"""
# 接收Authorization Code
code = request.GET.get('code')
if not code:
return http.HttpResponseBadRequest('缺少code')
pass
3. OAuth2.0认证获取openid
- 使用code向QQ服务器请求access_token
- 使用access_token向QQ服务器请求openid
class QQAuthUserView(View):
"""用户扫码登录的回调处理"""
def get(self, request):
"""Oauth2.0认证"""
# 提取code请求参数
code = request.GET.get('code')
if not code:
return http.HttpResponseBadRequest('缺少code')
# 创建工具对象
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET, redirect_uri=settings.QQ_REDIRECT_URI)
try:
# 使用code向QQ服务器请求access_token
access_token = oauth.get_access_token(code)
# 使用access_token向QQ服务器请求openid
openid = oauth.get_open_id(access_token)
except Exception as e:
logger.error(e)
http.JsonResponse('code': 400, 'errmsg': 'oauth2.0认证失败, 即获取qq信息失败')
pass
2.5 openid是否绑定用户的处理
1. 判断openid是否绑定过用户
使用openid查询该QQ用户是否在美多商城中绑定过用户。
try:
oauth_user = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
# 如果openid没绑定美多商城用户
pass
else:
# 如果openid已绑定美多商城用户
pass
2. openid已绑定用户的处理
如果openid已绑定美多商城用户,直接生成状态保持信息,登录成功,并重定向到首页。
try:
# 查看是否有 openid 对应的用户
oauth_qq = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
# 如果 openid 没绑定美多商城用户
# 请查看: openid 未绑定用户的处理
pass
else:
# 如果 openid 已绑定美多商城用户
# 根据 user 外键, 获取对应的 QQ 用户(user)
user = oauth_qq.user
# 实现状态保持
login(request, user)
# 创建重定向到主页的对象
response = http.JsonResponse('code': 0, 'errmsg': 'ok')
# 将用户信息写入到 cookie 中,有效期14天
response.set_cookie('username', user.username, max_age=3600 * 24 * 14)
# 返回响应
return response
3. openid未绑定用户的处理
- 为了能够在后续的绑定用户操作中前端可以使用openid,在这里将openid签名后响应给前端。
- openid属于用户的隐私信息,所以需要将openid签名处理,避免暴露。
try:
# 查看是否有 openid 对应的用户
oauth_qq = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
# 如果 openid 没绑定美多商城用户,进入这里:
# 使用加密类加密 openid
access_token = generate_access_token('openid':openid)
# 注意: 这里一定不能返回 0 的状态码. 否则不能进行绑定页面
return http.JsonResponse('code':300,'errmsg':'ok','access_token':access_token)
else:
...
4. 补充itsdangerous的使用
itsdangerous
模块的参考资料链接ItsDangerous — ItsDangerous Documentation (1.1.x)安装:
pip install itsdangerous
TimedJSONWebSignatureSerializer
的使用
- 使用
TimedJSONWebSignatureSerializer
可以生成带有有效期的token
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from django.conf import settings
# serializer = Serializer(秘钥, 有效期秒)
serializer = Serializer(settings.SECRET_KEY, 300)
# serializer.dumps(数据), 返回bytes类型
token = serializer.dumps('mobile': '18512345678')
token = token.decode()
# 检验token
# 验证失败,会抛出itsdangerous.BadData异常
serializer = Serializer(settings.SECRET_KEY, 300)
try:
data = serializer.loads(token)
except BadData:
return None
补充:openid签名处理
oauth.utils.py
from itsdangerous import BadData
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from django.conf import settings
def generate_access_token(openid):
"""
签名openid
:param openid: 用户的openid
:return: access_token
"""
serializer = Serializer(settings.SECRET_KEY, expires_in=3600)
data = 'openid': openid
token = serializer.dumps(data)
return token.decode()
def check_access_token(access_token):
"""
提取openid
:param access_token: 签名后的openid
:return: openid or None
"""
serializer = Serializer(settings.SECRET_KEY, expires_in=3600)
try:
data = serializer.loads(access_token)
except BadData:
return None
else:
return data.get('openid')
2.6 openid绑定用户实现
类似于用户注册的业务逻辑
- 当用户输入的手机号对应的用户已存在
- 直接将该已存在用户跟
openid
绑定- 当用户输入的手机号对应的用户不存在
- 新建一个用户,并跟
openid
绑定
from apps.oauth.models import OAuthQQUser
from django.contrib.auth import login
import json
import re
from apps.users.models import User
from django.db import DatabaseError
from django_redis import get_redis_connection
from .utils import check_access_token
class QQUserView(View):
"""用户扫码登录的回调处理"""
def get(self, request):
"""Oauth2.0认证"""
pass
def post(self, request):
"""美多商城用户绑定到openid"""
# 1.接收参数
data_dict = json.loads(request.body.decode())
mobile = data_dict.get('mobile')
password = data_dict.get('password')
sms_code_client = data_dict.get('sms_code')
access_token = data_dict.get('access_token')
# 2.校验参数
# 判断参数是否齐全
if not all([mobile, password, sms_code_client]):
return http.JsonResponse('code': 400,
'errmsg': '缺少必传参数')
# 判断手机号是否合法
if not re.match(r'^1[3-9]\\d9$', mobile):
return http.JsonResponse('code': 400,
'errmsg': '请输入正确的手机号码')
# 判断密码是否合格
if not re.match(r'^[0-9A-Za-z]8,20$', password):
return http.JsonResponse('code': 400,
'errmsg': '请输入8-20位的密码')
# 3.判断短信验证码是否一致
# 创建 redis 链接对象:
redis_conn = get_redis_connection('code')
# 从 redis 中获取 sms_code 值:
sms_code_server = redis_conn.get('sms_%s' % mobile)
# 判断获取出来的有没有:
if sms_code_server is None:
# 如果没有, 直接返回:
return http.JsonResponse('code': 400,
'errmsg': '验证码失效')
# 如果有, 则进行判断:
if sms_code_client != sms_code_server.decode():
# 如果不匹配, 则直接返回:
return http.JsonResponse('code': 400,
'errmsg': '输入的验证码有误')
# 调用我们自定义的函数, 检验传入的 access_token 是否正确:
# 错误提示放在 sms_code_errmsg 位置
openid = check_access_token(access_token)
if not openid:
return http.JsonResponse('code': 400,
'errmsg': '缺少openid')
# 4.保存注册数据
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.JsonResponse('code': 400,
'errmsg': '输入的密码不正确')
# 5.将用户绑定 openid
try:
OAuthQQUser.objects.create(openid=openid,
user=user)
except DatabaseError:
return http.JsonResponse('code': 400,
'errmsg': '往数据库添加数据出错')
# 6.实现状态保持
login(request, user)
# 7.创建响应对象:
response = http.JsonResponse('code': 0,
'errmsg': 'ok')
# 8.登录时用户名写入到 cookie,有效期14天
response.set_cookie('username',
user.username,
max_age=3600 * 24 * 14)
# 9.响应
return response
补充:openid签名处理
oauth.utils.py
from itsdangerous import BadData
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from django.conf import settings
from apps.oauth import constants
def generate_access_token(openid):
"""
签名openid
:param openid: 用户的openid
:return: access_token
"""
serializer = Serializer(settings.SECRET_KEY, expires_in=constants.ACCESS_TOKEN_EXPIRES)
data = 'openid': openid
token = serializer.dumps(data)
return token.decode()
def check_access_token(access_token):
"""
提取openid
:param access_token: 签名后的openid
:return: openid or None
"""
serializer = Serializer(settings.SECRET_KEY, expires_in=constants.ACCESS_TOKEN_EXPIRES)
try:
data = serializer.loads(access_token)
except BadData:
return None
else:
return data.get('openid')
以上是关于美多商城项目之用户登录:账号登录QQ登录的主要内容,如果未能解决你的问题,请参考以下文章