飞行的猪哼哼

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一:用户名登录和用户名展示:

# 用户名登陆接口实现
class LoginView(View):
    def post(self, request):
        data = json.loads(request.body.decode())
        username = data.get("username")
        password = data.get("password")
        remembered = data.get("remembered")

        user = authenticate(request, username=username, password=password)
        if user is None:
            return JsonResponse({
                "code": 400,
                "errmsg": "用户名或者密码错误"
            })
        login(request, user)

        if remembered != True:
            request.session.set_expiry(0)
        else:
            request.session.set_expiry(None)
        response = JsonResponse({
            "code": 0,
            "errmsg": "ok"
        })
        response.set_cookie('username', user.username, max_age=3600 * 24 * 14)
        
        return response

视图:

re_path(r"^login/$", views.LoginView.as_view()),

二:多账号登录:(自定义认证后端)

users/utils

from django.contrib.auth.backends import ModelBackend

from django.db.models import Q
from apps.users.models import User


class LoginBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            user = User.objects.get(Q(username=username)|Q(mobile=username))

        except Exception as e:
            return None

        if user.check_password(password):
            return user

配置文件中:

AUTHENTICATION_BACKENDS = ['apps.users.utils.LoginBackend']

三:退出登录:

class LoginOutView(View):
    def delete(self, request):
        # 1:清理session
        logout(request)
        response = JsonResponse({"code": 0, "errmsg": "ok"})
        # 2:清除前端的cookie
        response.delete_cookie("username")
        return response

四:用户中心页面展示:(自定义登录后端)

用户登陆了,可以访问用户中心页面,没有登录不允许访问,怎样判断用户是否登录了呢??

from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import JsonResponse


class LoginRequiredJSONMixin(LoginRequiredMixin):
    def handle_no_permission(self):
        return JsonResponse({'code': 400, 'errmsg': '用户未登录'})
# 用户中心页面展示:
class UserCenterDataView(LoginRequiredJSONMixin, View):

    def get(self, request):
        return JsonResponse({
            'code': 0,
            'errmsg': '个人中心',
             "info_data":{
                 "username" : request.user.username,
                "mobile" : request.user.mobile,
                "email" : request.user.email,
                "email_active" : request.user.email_active
                }
            })

======================================================================

QQ登录:

一:子应用准备工作:

1:建立一个子应用:oauth,注册子应用,总路由和子路由

1:建一个子应用
(django_env) python@ubuntu:~/Desktop/meiduo_project/meiduo_mall/apps$ python3 ../manage.py startapp oauth
2:注册子应用,dev.py中
INSTALLED_APPS = [
    'apps.oauth',
]
3:注册总路由:meiduo_mall/urls
urlpatterns = [
    re_path("", include("apps.oauth.urls")),
]
4:注册子路由:apps/oauth/urls
urlpatterns = [
    
]

二:QQ登陆的流程分析:

QQ登录有三个过程:
过程一:用户点击美多商城的QQ登录,此时没多商城后台向QQ申请QQ登录的url,QQ将QQ登录的链接返回给美多商城,美多商城再把这个链接返回给用户。

中间过程:用户扫码登录QQ,QQ返回给用户浏览器一个带有用户身份的code。

过程二: 用户拿到这个code后,再次请求美多商城。美多商城根据code向QQ要access_tocken,QQ返回给美多商城access_tocken。美多商城根据access_tocken 再向QQ要openid。最终美多商城拿到用户的openid。

过程三:过程二已经确认用户的身份信息,现在美多商城根据openid查找用户,如果找到用户,则构建响应,状态保持即可。如果在数据库中没有对应的人,则返回填写用户信息的页面。用户填完信息,提交到美多商城后台有两种情况:

情况一:用户从来没有注册过美多商城,则新建用户,并且绑定用户的openid。
情况二:用户已经注册过美多商城了,但是从来没有绑定过QQ,则直接绑定用户的openid即可。
在这里插入图片描述

三:模型类准备工作:

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

auto_now_add:创建或添加对象时自动添加时间, 修改或更新对象时, 不会更改时间
auto_now:凡是对对象进行操作(创建/添加/修改/更新),时间都会随之改变

2:定义登录使用的模型类:oauth/models.py

from django.db import models
from meiduo_mall.utils.models import BaseModel

# 定义QQ登录的模型类: 
class OAuthQQUser(BaseModel):
    """QQ登录用户数据"""

    # user 是个外键, 关联对应的用户
    user = models.ForeignKey('users.User',  on_delete=models.CASCADE, verbose_name='用户')
    # qq 发布的用户身份id
    openid = models.CharField(max_length=64,  verbose_name='openid', db_index=True)

    class Meta:
        db_table = 'tb_oauth_qq'

3:迁移建表:

(django_env) python@ubuntu:~/Desktop/meiduo_project/meiduo_mall$ python manage.py 
(django_env) python@ubuntu:~/Desktop/meiduo_project/meiduo_mall$ python manage.py migrate

四:QQ登录工具的安装和参数的配置:

pip install QQLoginTool -i https://pypi.tuna.tsinghua.edu.cn/simple

dev.py:QQ登录参数

# QQ登录参数
# 我们申请的 客户端id
QQ_CLIENT_ID = '101474184'
# 我们申请的 客户端秘钥
QQ_CLIENT_SECRET = 'c6ce949e04e12ecc909ae6a8b09b637c'
# 我们申请时添加的: 登录成功后回调的路径
QQ_REDIRECT_URI = 'http://www.meiduo.site:8080/oauth_callback.html'

五:功能实现:

1:第一个视图:获取openid : oauth/views.py

from QQLoginTool.QQtool import OAuthQQ
from django.conf import settings
from django.http import JsonResponse
from django.views import View


class QQURLView(View):
    def get(self, request):

        # 1:登陆成功后跳转的网页
        next = request.GET.get("next")

        # 2: 创建QQ登陆对象
        oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,
                        client_secret=settings.QQ_CLIENT_SECRET,
                        redirect_uri=settings.QQ_REDIRECT_URI,
                        state=next)
        # 3:使用QQ对象拿到QQ返回的url
        login_url = oauth.get_qq_url()

        return JsonResponse({
            "code": 0,
            "errmsg": "ok",
            "login_url": login_url,
        })

路由:

from django.urls import path

from oauth import views

urlpatterns = [
    path('qq/authorization/', views.QQURLView.as_view()),
]

2:第二个视图:
2.1:根据code -->access_tocken ----> openid

class QQUserView(View):
    def get(self, request):
        
        # 1:获取code
        code = request.GET.get("code")
        if not code:
            return JsonResponse({
                "code": 400,
                "errmsg": "缺失参数"
            })
        
        # 2:创建QQ登陆对象:
        oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,
                        client_secret=settings.QQ_CLIENT_SECRET,
                        redirect_uri=settings.QQ_REDIRECT_URI,
                        state=next)
        
        # 3:获取openid
        try:
            access_token = oauth.get_access_token(code)
            
            openid = oauth.get_open_id(access_token)
        
        except Exception as e:
            return JsonResponse({
                "code": 400,
                "errmsg": "无法获取用户的access_tocken",
            })
        
        else:
            pass

2.2:如果用户存在则状态保持,如果不存在则将openid加密返回给用户。

else:
            # 4:根据openid判断用户是否绑定过用户
            try:
                oauth_qq = OAuthQQUser.objects.get(openid=openid)
            except Exception as e:
                # 用户没有绑定美多商城用户,使用加密类加密用户的openid,返回给用户,前端会让顾客填写信息,然后再带着这个openid返回
                
                pass
            
            else:# 4.1: 数据库中查询到了用户
                user = oauth_qq.user
                
                # 状态保持与设置cookie
                login(request, user)
                
                response = JsonResponse({
                    "code": 0,
                    "errmsg": "ok"
                })
                response.set_cookie("username", user.username, max_age=3600 * 24 * 14)
                
                return response

此时出现一个问题:需要对openid进行加密,下一次用户再带回来时,需要解密。这就需要加密解密的类。

2.3:itsdangerous加密模块的使用:
安装:pip install itsdangerous -i https://pypi.tuna.tsinghua.edu.cn/simple
meiduo_mall.utils.secret.py

from django.conf import settings
# 使用 TimedJSONWebSignatureSerializer 对象 可以设置有效期
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
class SecretOauth(object):
    def __init__(self):
        # 1.根据 签名(secret_key) 创建序列化对象
        self.serializer = Serializer(secret_key=settings.SECRET_KEY, expires_in=24 * 15 * 60)

    # 加密
    def dumps(self, content_dict):
        # 1. 通过 dumps方法 加密数据
        result = self.serializer.dumps(content_dict)
        # 2. result是bytes类型转换成 str
        return result.decode()

    # 解密
    def loads(self, content_dict):
        try:
            # 1. 通过 loads方法 解密数据
            result = self.serializer.loads(content_dict) 
        except Exception as e:
            print(e)
            # 解密失败返回None
            return None

        # 2. 返回解密完毕数据
        return result

2.4:使用加密类加密返回:

try:
                oauth_qq = OAuthQQUser.objects.get(openid=openid)
            except Exception as e:
                # 用户没有绑定美多商城用户,使用加密类加密用户的openid,返回给用户,前端会让顾客填写信息,然后再带着这个openid返回
                access_token = SecretOauth().dumps({'openid': openid})
                # 注意: 这里一定不能返回 0 的状态码. 否则不能进行绑定页面
                return JsonResponse({'code': 300, 'errmsg': 'ok', 'access_token': access_token})

2.5:完整的视图:

from QQLoginTool.QQtool import OAuthQQ
from django.conf import settings
from django.contrib.auth import login
from django.http import JsonResponse
from django.views import View
from meiduo_mall.utils.secret import SecretOauth
from apps.oauth.models import OAuthQQUser


class QQURLView(View):
    def get(self, request):

        # 1:登陆成功后跳转的网页
        next = request.GET.get("next")

        # 2: 创建QQ登陆对象
        oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,
                        client_secret=settings.QQ_CLIENT_SECRET,
                        redirect_uri=settings.QQ_REDIRECT_URI,
                        state=next)
        # 3:使用QQ对象拿到QQ返回的url
        login_url = oauth.get_qq_url()

        return JsonResponse({
            "code": 0,
            "errmsg": "ok",
            "login_url": login_url,
        })


class QQUserView(View):
    def get(self, request):

        # 1:获取code
        code = request.GET.get("code")
        if not code:
            return JsonResponse({
                "code": 400,
                "errmsg": "缺失参数"
            })

        # 2:创建QQ登陆对象:
        oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,
                        client_secret=settings.QQ_CLIENT_SECRET,
                        redirect_uri=settings.QQ_REDIRECT_URI,
                        state=next)

        # 3:获取openid
        try:
            access_token = oauth.get_access_token(code)

            openid = oauth.get_open_id(access_token)

        except Exception as e:
            return JsonResponse({
                "code": 400,
                "errmsg": "无法获取用户的access_tocken",
            })

        else:
            # 4:根据openid判断用户是否绑定过用户
            try:
                oauth_qq = OAuthQQUser.objects.get(openid=openid)
            except Exception as e:
                # 用户没有绑定美多商城用户,使用加密类加密用户的openid,返回给用户,前端会让顾客填写信息,然后再带着这个openid返回
                access_token = SecretOauth().dumps({'openid': openid})
                # 注意: 这里一定不能返回 0 的状态码. 否则不能进行绑定页面
                return JsonResponse({'code': 300, 'errmsg': 'ok', 'access_token': access_token})
            
            else:# 4.1: 数据库中查询到了用户
                user = oauth_qq.user
                
                # 状态保持与设置cookie
                login(request, user)
                
                response = JsonResponse({
                    "code": 0,
                    "errmsg": "ok"
                })
                response.set_cookie("username", user.username, max_age=3600 * 24 * 14)
                
                return response
                
                

2.6:路由:

 path('oauth_callback/', views.QQUserView.as_view()),

3:第二个视图的第二部分:
核心代码:

 # 4.保存注册数据
        try:
            user = User.objects.get(mobile=mobile)
        except Exception as e:
            # 用户不存在,新建用户
            user = User.objects.create_user(username=mobile,
                                            password=password,
                                            mmobile=mobile)
        else:
            # 如果用户存在,检查用户密码
            if not user.check_password(password):
                return JsonResponse({'code': 400,
                                          'errmsg': '输入的密码不正确'})
        # 5.将用户绑定 openid
        try:
            OAuthQQUser.objects.create(openid=openid,
                                       user=user)
        except DatabaseError:
            return JsonResponse({'code': 400,
                                      'errmsg': '往数据库添加数据出错'})

总体代码:

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 JsonResponse({'code': 400,
                                      'errmsg': '缺少必传参数'})

        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return JsonResponse({'code': 400,
                                      'errmsg': '请输入正确的手机号码'})

        # 判断密码是否合格
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return JsonResponse({'code': 400,
                                      'errmsg': '请输入8-20位的密码'})

        # 3.判断短信验证码是否一致
        # 创建 redis 链接对象:
        redis_conn = get_redis_connection('verify_code')

        # 从 redis 中获取 sms_code 值:
        sms_code_server = redis_conn.get('sms_%s' % mobile)

        # 判断获取出来的有没有:
        if sms_code_server is None:
            # 如果没有, 直接返回:
            return JsonResponse({'code': 400,
                                      'errmsg': '验证码失效'})
        # 如果有, 则进行判断:
        if sms_code_client != sms_code_server.decode():
            # 如果不匹配, 则直接返回:
            return JsonResponse({'code': 400,
                                      'errmsg': '输入的验证码有误'})

            # 调用我们自定义的函数, 检验传入的 access_token 是否正确:
        # 错误提示放在 sms_code_errmsg 位置
        openid = SecretOauth().loads(access_token).get('openid')
        print(openid)
        if not openid:
            return JsonResponse({'code': 400,
                                      'errmsg': '缺少openid'})
        # 4.保存注册数据
        try:
            user = User.objects.get(mmobile=mobile)
        except Exception as e:
            # 用户不存在,新建用户
            user = User.objects.create_user(username=mobile,
                                            password=password,
                                            mmobile=mobile)
        else:
            # 如果用户存在,检查用户密码
            if not user.check_password(password):
                return JsonResponse({'code': 400,
                                          'errmsg': '输入的密码不正确'})
        # 5.将用户绑定 openid
        try:
            OAuthQQUser.objects.create(openid=openid,
                                       user=user)
        except DatabaseError:
            return JsonResponse({'code': 400,
                                      'errmsg': '往数据库添加数据出错'})
        # 6.实现状态保持
        login(request, user)

        # 7.创建响应对象:
        response = JsonResponse({'code': 0,
                                      'errmsg': 'ok'})

        # 8.登录时用户名写入到 cookie,有效期14天
        response.set_cookie('username',
                            user.username,
                            max_age=3600 * 24 * 14)

        # 9.响应
        return response

六:测试工作:
6.1:用户没有注册过美多,直接QQ登录。
首先关闭云通讯,改成打印方式:(不浪费钱,云通讯前面已经实现了)

 # CCP().send_template_sms(
        #     mobile,
        #     [sms_code, 10],
        #     1,
        # )

        print(sms_code)

删除数据库tb_user所有的字段
在这里插入图片描述
点击QQ登录:
在这里插入图片描述
扫码登录:
在这里插入图片描述
注册登录:
在这里插入图片描述
结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.2:现在有了账号直接扫码验证,则现象是可以直接登录进去。
就是用刚才QQ再次登录。
6.3:如果原来有美多商城账号,现在用QQ登录呢?
先注册一个美多账号,没有关联QQ。
在这里插入图片描述
此时user表中有两个数据:
在这里插入图片描述
但是oauth_qq这个关联QQ的表没有第二个数据:
在这里插入图片描述
再次QQ登录
在这里插入图片描述
进入成功
在这里插入图片描述
在这里插入图片描述
出现两条记录。

posted on 2020-09-18 20:30  飞行的猪哼哼  阅读(61)  评论(0)    收藏  举报