User login of Mido Mall (QQ login)

Posted by prslou on Sat, 22 Jan 2022 12:15:32 +0100

2, QQ login

2.1 QQ login development document

QQ login: the so-called third-party login means that the user can successfully log in to the project without entering a password in the project.

1. Application steps for QQ Internet developers

If you want to realize QQ login, you need to become a developer of QQ Internet and pass the audit.

2. Application steps of QQ Internet application

After becoming a QQ Internet developer, you also need to create an application, that is, obtain the application ID corresponding to QQ Internet of this project.

3. Website docking QQ login steps

QQ Internet provides development documents to help developers realize QQ login.

4. QQ login process analysis

5. Key points of knowledge

  1. When interfacing with the third-party platform, we must carefully read the documents provided by the third-party platform. There must be instructions for the interface in the document to facilitate our development.

2.2 define QQ login model class

After QQ login is successful, we need to associate QQ users with Mido mall users to facilitate the next QQ login, so we choose to use MySQL database for storage.

1. Define model class base class

In order to supplement the two fields of creation time and update time of model class data in the project, we need to define the base class of model class. In Meiduo_ mall. utils/models. Create a model class base class in the. Py file.

from django.db import models

class BaseModel(models.Model):
    """Supplementary fields for model classes"""

    create_time = models.DateTimeField(auto_now_add=True, verbose_name="Creation time")
    update_time = models.DateTimeField(auto_now=True, verbose_name="Update time")

    class Meta:
        abstract = True  # Description is an abstract model class, which is used for inheritance and use. BaseModel tables will not be created during database migration

2. Define QQ login model class

Create a new application oauth to realize QQ third-party authentication login. [a new sub application]

# oauth
url(r'^oauth/', include('oauth.urls')),

In OAuth / models Py defines the association relationship between QQ identity (openid) and User model class User

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


# Create your models here.
class OAuthQQUser(BaseModel):
    """QQ Login user data"""
    user = models.ForeignKey('users.User', on_delete=models.CASCADE, verbose_name='user')
    openid = models.CharField(max_length=64, verbose_name='openid', db_index=True)

    class Meta:
        db_table = 'tb_oauth_qq'
        verbose_name = 'QQ Login user data'
        verbose_name_plural = verbose_name

3. Migrate QQ login model class

$ python manage.py makemigrations
$ python manage.py migrate

2.3 QQLoginTool

1. Introduction to qqlogintool

  • The tool encapsulates the request operation of connecting to QQ interconnection interface during QQ login. It can be used to quickly realize QQ login.

2. QQLoginTool installation

pip install QQLoginTool

3. QQLoginTool instructions

1. Import

from QQLoginTool.QQtool import OAuthQQ

2. Initialize OAuthQQ object

oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET, redirect_uri=settings.QQ_REDIRECT_URI, state=next)

3. Get the QQ login code scanning page, and get the Authorization Code after scanning the code

login_url = oauth.get_qq_url()

4. Obtain Access Token through Authorization Code

access_token = oauth.get_access_token(code)

5. Obtain OpenID through Access Token

openid = oauth.get_open_id(access_token)

2.4 OAuth2.0 authentication get openid

Pending business logic

# Extract code request parameters
# Use code to request access from QQ server_ token
# Using access_token requests openid from QQ server
# Use openid to query whether the QQ user has bound a user in Meiduo mall
# If openid has been bound to the user of Mido mall, the JWT token is directly generated and returned
# If openid is not bound to Mido mall users, create users and bind to openid

1. Get QQ login code scanning page

1. Request method

optionprogramme
Request methodGET
Request address/qq/login/

Total route:

Sub route:

from django.conf.urls import url
from . import views

urlpatterns = [
    #  Provide QQ login code scanning page
    url(r'^qq/login/$', views.QQAuthURLView.as_view()),

]

2. Request parameters: query parameters

Parameter nametypeIs it necessary to passexplain
nextstringnoIt is used to record the web address entered after QQ login is successful

3. Response result: JSON

fieldexplain
codeStatus code
errmsgerror message
login_urlQQ login code scanning page link

4. Back end logic implementation

from django.shortcuts import render
from django.views import View
from QQLoginTool.QQtool import OAuthQQ
from django import http
from django.conf import settings
from meiduo_mall.utils.response_code import RETCODE

# Create your views here.


class QQAuthURLView(View):
    """provide QQ Login page URL--
    https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=xxx&redirect_uri=xxx&state=xxx"""

    def get(self, request):
        #  next indicates where to enter the login page. After successful login in the future, you will automatically return to that page
        next = request.GET.get('next')
        #  Get QQ login URL
        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': RETCODE.OK, 'errmsg': 'OK', 'login_url': login_url})

5.QQ login parameters

QQ_CLIENT_ID = '101518219'
QQ_CLIENT_SECRET = '418d84ebdc7241efb79536886ae95224'
QQ_REDIRECT_URI = 'http://www.meiduo.site:8000/oauth_callback'

2. Receive Authorization Code

Tips:

  • After the user logs in successfully on QQ, QQ will redirect the user to the callback URL configured by us.
  • When QQ redirects to the callback URL, it will pass us an Authorization Code.
  • We need to get the Authorization Code and complete oauth2 Get openid for 0 authentication.
  • In this project, the callback website configured when we apply for QQ login development qualification is:
    • http://www.meiduo.site:8000/oauth_callback
  • The complete website of QQ Internet redirection is:
    • http://www.meiduo.site:8000/oauth_callback/?code=AE263F12675FA79185B54870D79730A7&state=%2F [%2f refers to the root path of]
class QQAuthUserView(View):
    """Callback processing of user code scanning login"""

    def get(self, request):
        """Oauth2.0 authentication"""
        # Receive Authorization Code
        code = request.GET.get('code')
        if not code:
            return http.HttpResponseForbidden('lack code')
        pass
url(r'^oauth_callback/$', views.QQAuthUserView.as_view()),

3. OAuth2.0 authentication get openid

  1. Use code to request access from QQ server_ token
  2. Using access_token requests openid from QQ server
class QQAuthUserView(View):
    """Callback processing of user code scanning login"""

    def get(self, request):
        """Oauth2.0 authentication"""
        # Extract code request parameters
        code = request.GET.get('code')
        if not code:
            return http.HttpResponseForbidden('lack code')

        # Create tool object
        oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET, redirect_uri=settings.QQ_REDIRECT_URI)

        try:
            # Use code to request access from QQ server_ token
            access_token = oauth.get_access_token(code)

            # Using access_token requests openid from QQ server
            openid = oauth.get_open_id(access_token)
        except Exception as e:
            logger.error(e)
            return http.HttpResponseServerError('OAuth2.0 Authentication failed')
        pass

4. Native binding www.meiduo.com Site domain name

1.ubuntu system or Mac system

edit /etc/hosts

2.Windows system

edit C:\Windows\System32\drivers\etc\hosts

2.5 processing of whether openid is bound to the user

1. Judge whether openid is bound to the user

Use openid to query whether the QQ user has bound a user in Meiduo mall.

from .models import OAuthQQUser

......
try:
    oauth_user = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
    # If openid is not bound to Mido mall users
    pass
else:
    # If openid is bound to Mido mall user
    pass

2. Processing of openid bound user

If openid has been bound to the user of Mido mall, the status maintenance information is directly generated, the login is successful, and the user is redirected to the home page.

from django.contrib.auth import login
from django.shortcuts import redirect
from django.urls import reverse


......
try:
    oauth_user = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
    # If openid is not bound to Mido mall users
    pass
else:
    # If openid is bound to Mido mall user
    # Implementation state retention
    qq_user = oauth_user.user
    login(request, qq_user)

    # Response results
    next = request.GET.get('state')
    response = redirect(next)

    # When logging in, the user name is written into the cookie, which is valid for 15 days
    response.set_cookie('username', qq_user.username, max_age=3600 * 24 * 15)

    return response

3. Processing of openid unbound user

  • To enable the front end to use openid in subsequent binding user operations, the openid is signed and responded to the front end.
  • openid belongs to the user's private information, so it needs to be signed to avoid exposure.
try:
    oauth_user = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
    # If openid is not bound to Mido mall users
    access_token = generate_eccess_token(openid)
    context = {'access_token': access_token}
    return render(request, 'oauth_callback.html', context)
else:
    # If openid is bound to Mido mall user
    # Implementation state retention
    qq_user = oauth_user.user
    login(request, qq_user)

    # Redirect to home page
    response = redirect(reverse('contents:index'))

    # When logging in, the user name is written to the cookie, which is valid for 15 days
    response.set_cookie('username', qq_user.username, max_age=3600 * 24 * 15)

    return response

oauth_ callback. Rendering access in HTML_ token

<input v-model="access_token" type="hidden" name="access_token" value="{{ access_token }}">

4. Supplement the use of itsdangerous [the encryption here is reversible, and the encryption of password is irreversible]

  • Resources link for its dangerous module http://itsdangerous.readthedocs.io/en/latest/

  • Installation: pip install itsdangerous

  • Use of TimedJSONWebSignatureSerializer

    • The TimedJSONWebSignatureSerializer can be used to generate a token with a valid period

Example code:

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from django.conf import settings

# Serializer = serializer (secret key, valid for seconds)
serializer = Serializer(settings.SECRET_KEY, 300)
# serializer. Dumps (data), which returns the type of bytes
token = serializer.dumps({'mobile': '18512345678'})
token = token.decode()

# Check token
# If the verification fails, an itsdangerous. Error will be thrown Baddata exception
serializer = Serializer(settings.SECRET_KEY, 300)
try:
    data = serializer.loads(token)
except BadData:
    return None

Supplement: openid signature processing

  • oauth.utils.py new utils py
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from django.conf import settings
from . import constants_oauth
from itsdangerous import BadData


def generate_access_token(openid):
    """
    autograph openid
    :param openid: User's openid
    :return: access_token
    """
    #  Create serializer object
    serializer = Serializer(settings.SECRET_KEY, expires_in=constants_oauth.ACCESS_TOKEN_EXPIRES)
    #  Prepare dictionary to be serialized
    data = {'openid': openid}
    #  Call dumps method for serialization
    token = serializer.dumps(data)
    return token.decode()

New tool file constants_oauth.py:

# access_ Valid period of token, in seconds
ACCESS_TOKEN_EXPIRES = 600

2.6 openid binding user implementation

Business logic similar to user registration

  • When the user enters the mobile phone number, the corresponding user already exists
    • Bind the existing user to openid directly
  • When the mobile phone number entered by the user does not exist
    • Create a new user and bind it with openid
from .utils import generate_access_token
import re
from django_redis import get_redis_connection
from meiduo_mall.apps.users.models import User
from .utils import check_access_token


class QQAuthUserView(View):
    """Callback processing of user code scanning login"""

    def get(self, request):
        """Oauth2.0 authentication"""
        ......

    def post(self, request):
        """Mido mall users bound to openid"""
        # Receive parameters
        mobile = request.POST.get('mobile')
        pwd = request.POST.get('password')
        sms_code_client = request.POST.get('sms_code')
        access_token = request.POST.get('access_token')

        # Calibration parameters
        # Judge whether the parameters are complete
        if not all([mobile, pwd, sms_code_client]):
            return http.HttpResponseForbidden('Missing required parameters')
        # Judge whether the mobile phone number is legal
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('Please enter the correct mobile phone number')
        # Judge whether the password is qualified
        if not re.match(r'^[0-9A-Za-z]{8,20}$', pwd):
            return http.HttpResponseForbidden('Please enter 8-20 Bit password')
        # Judge whether the SMS verification code is consistent
        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':'Invalid SMS verification code'})
        if sms_code_client != sms_code_server.decode():
            return render(request, 'oauth_callback.html', {'sms_code_errmsg': 'Error in entering SMS verification code'})
        # Judge whether openid is valid: put the error prompt in sms_code_errmsg location
        openid = check_access_token(access_token)
        if not openid:
            return render(request, 'oauth_callback.html', {'openid_errmsg': 'invalid openid'})

        # Save registration data
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            # User does not exist, create a new user
            user = User.objects.create_user(username=mobile, password=pwd, mobile=mobile)
        else:
            # If the user exists, check the user password
            if not user.check_password(pwd):
                return render(request, 'oauth_callback.html', {'account_errmsg': 'Wrong user name or password'})

        # Bind user to openid
        try:
            OAuthQQUser.objects.create(openid=openid, user=user)
        except DatabaseError:
            return render(request, 'oauth_callback.html', {'qq_login_errmsg': 'QQ Login failed'})

        # Implementation state retention
        login(request, user)

        # Response binding result
        next = request.GET.get('state')
        response = redirect(next)

        # When logging in, the user name is written to the cookie, which is valid for 15 days
        response.set_cookie('username', user.username, max_age=3600 * 24 * 15)

        return response

utils.py

def check_access_token(access_token_openid):
    """Deserialization access_token_openid"""
    #  Create serialized object
    serializer = Serializer(settings.SECRET_KEY, expires_in=constants_oauth.ACCESS_TOKEN_EXPIRES)
    #  Deserialize openid ciphertext
    try:
        data = serializer.loads(access_token_openid)
    except BadData:  # openid expired
        return None
    else:
        #  Return openid plaintext
        return data.get('openid')

Topics: Python Web Development Django