• QQ第三方登录python_web开发_django框架


    准备工作

    1. 成为QQ互联的开发者 参考链接:

    <http://wiki.connect.qq.com/%E6%88%90%E4%B8%BA%E5%BC%80%E5%8F%91%E8%80%85>

    2. 审核通过后,创建应用,即获取本项目对应与QQ互联的应用ID 参考链接:http://wiki.connect.qq.com/__trashed-2

    3. 在 models.py 中定义QQ身份(openid)与用户模型类User的关联关系

    class OAuthQQUser(models.Model):
       """
      QQ登录用户数据
      """
       user = models.ForeignKey('users.User', on_delete=models.CASCADE, verbose_name='用户')
       openid = models.CharField(max_length=64, verbose_name='openid', db_index=True)
       create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
       update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
       class Meta:
           db_table = 'tb_oauth_qq'
           verbose_name = 'QQ登录用户数据'
           verbose_name_plural = verbose_name  # 单复数同名

    4. QQ登录SDK使用

    初始化OAuthQQ对象

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

    获取QQ登录扫码页面,扫码后得到Authorization Code

    login_url = oauth.get_qq_url()

    通过Authorization Code获取Access Token

    access_token = oauth.get_access_token(code)

    通过Access Token获取OpenID

    openid = oauth.get_open_id(access_token)

    实现流程

    具体流程

    1. 返回QQ登录网址的视图

      1.1 在配置文件中添加关于QQ登录的应用开发信息

      # QQ登录参数
      QQ_CLIENT_ID = 'xxxx'  # ID
      QQ_CLIENT_SECRET = 'xxxxxxxxxxxx'  # 密钥
      QQ_REDIRECT_URI = 'http://www.xxxx.xxx/oauth_callback.html'  # 回调域

      1.2 接口设计

      • 请求方式:GET /oauth/qq/statues/?state=xxx

      • 请求参数:查询字符串参数

        参数名类型是否必须说明
        state str 用户QQ登录成功后进入的网址
      • 返回数据:JSON

        {"login_url": oauth.get_qq_url()}
        返回值类型是否必须说明
        login_url str qq登录网址

      1.3 逻辑实现

      class QQAuthURLView(APIView):
         """
        提供QQ登录页面网址

        """
         def get(self, request):

             # state表示从哪个页面进入到的登录页面,将来登录成功后,就自动回到那个页面
             state= request.query_params.get('state')
             if not state:
                 state= '/'

             # 获取QQ登录页面网址
             oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET, redirect_uri=settings.QQ_REDIRECT_URI, state=state)
             login_url = oauth.get_qq_url()

             return Response({'login_url': login_url})
    1. OAuth认证

      准备oauth_callback回调页,用于扫码后接受Authorization Code

      通过Authorization Code获取Access Token

      通过Access Token获取OpenID

      2.1 接口设计

      • 请求方式:GET /oauth/qq/users/?code=xxx

      • 请求参数: 查询字符串参数

        参数名类型是否必须说明
        code str qq返回的授权凭证code
      • 返回数据

        {"access_token": xxxx,}

        {
           "token": "xxx",
           "username": "python",
           "user_id": 1
        }
        返回值类型是否必须说明
        access_token str 用户是第一次使用QQ登录时返回,其中包含openid,用于绑定身份使用,注意这个是我们自己生成的
        token str 用户不是第一次使用QQ登录时返回,登录成功的JWT token
        username str 用户不是第一次使用QQ登录时返回,用户名
        user_id int 用户不是第一次使用QQ登录时返回,用户id

      2.2 逻辑实现

      oauth/views.py

      class QQAuthUserView(GenericAPIView):
         """用户扫码登录的回调处理"""

         # 指定序列化器
         serializer_class = serializers.QQAuthUserSerializer

         def get(self, request):
             # 提取code请求参数
             code = request.query_params.get('code')
             if not code:
                 return Response({'message':'缺少code'},
                                 status=status.HTTP_400_BAD_REQUEST)

             # 创建工具对象
             oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,
                             client_secret=settings.QQ_CLIENT_SECRET,
                             redirect_uri=settings.QQ_REDIRECT_URI)

             try:
                 access_token = oauth.get_access_token(code)

                 # 3,通过access_token获取openid
                 openid = oauth.get_open_id(access_token)

                 # 4,通过openid查询oauthqq对象

                 try:
                     oauth_qq_user = OAuthQQUser.objects.get(openid=openid)
                 except OAuthQQUser.DoesNotExist:
                     # ①, 没有项目用户, 也没有OAuthQQUser用户
                     # ②, 有项目用户, 没有OAuthQQUser用户

                     # 5,qq用户没有和项目用户绑定过,加密openid,并返回
                     access_token_openid = generate_save_user_openid(openid)
                     return Response({"access_token": access_token_openid})

             except Exception:
                 return Response({"message": "请求qq服务器异常"},
                                 status=status.HTTP_400_BAD_REQUEST)

             # 6,oauth_qq_user存在,并且绑定过了美多用户
             user = oauth_qq_user.user

             # 7,组织数据,拼接token,返回响应
             jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
             jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
             payload = jwt_payload_handler(user)
             token = jwt_encode_handler(payload)

             return Response({
                 "user_id": user.id,
                 "username": user.username,
                 "token": token
            })

      oauth/utils.py** 中准备序列化 OpenID 的工具方法**

      # 对openid加密
      def generate_save_user_openid(openid):
         #1,创建TJWSSerializer对象
         serializer = TJWSSerializer(settings.SECRET_KEY,expires_in=300)

         #2,加密数据
         token = serializer.dumps({"openid":openid})

         #3,返回
         return token
    1. OpenID绑定用户

    如果用户是首次使用QQ登录,则需要绑定用户


    3.1 接口设计

    • 请求方式:POST /oauth/qq/users/

    • 请求参数:JSON 或 表单

      参数名类型是否必须说明
      mobile str 手机号
      password str 密码
      sms_code str 短信验证码
      access_token str 凭据(包含openid)
    • 返回数据:JSON

      返回值类型是否必须说明
      token str JWT token
      id int 用户id
      username str 用户名

    3.2 逻辑实现

    • oauth/views.py

      def post(self, request):
             # 1,获取数据
             dict_data = request.data

             # 2,获取序列化器,校验数据
             serializer = self.get_serializer(data=dict_data)
             serializer.is_valid(raise_exception=True)

             # 3,数据入库
             oauth_qq = serializer.save()

             # 4,组织,数据返回响应
             user = oauth_qq.user
             jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
             jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
             payload = jwt_payload_handler(user)
             token = jwt_encode_handler(payload)

             return Response({
                 "user_id": user.id,
                 "username": user.username,
                 "token": token
            })
    • 新建 oauth/serializers.py 文件

    • from rest_framework import serializers
      from .utils import check_save_user_openid
      from django_redis import get_redis_connection
      from users.models import User
      from .models import OAuthQQUser
      class QQAuthUserSerializer(serializers.Serializer):
         mobile = serializers.RegexField(label="手机号",regex=r"1[3-9]\d{9}")
         password = serializers.CharField(label="密码",min_length=8,max_length=20)
         sms_code = serializers.CharField(label="短信",min_length=6,max_length=6)
         access_token = serializers.CharField(label="token",min_length=1)

         def validate(self, attrs):
             """多字段校验"""
             #1,获取加密的openid
             access_token = attrs["access_token"]

             #2,调用方法解密openid,判断是否存在
             openid = check_save_user_openid(access_token)

             if not openid:
                 raise serializers.ValidationError("openid失效")

             #3,获取redis中的短信,判断为空,正确性
             sms_code = attrs["sms_code"]
             mobile = attrs["mobile"]
             redis_conn = get_redis_connection("code")
             redis_sms_code = redis_conn.get("sms_%s"%mobile)

             if not redis_sms_code:
                 raise serializers.ValidationError("短信验证码过期")

             if sms_code != redis_sms_code.decode():
                 raise serializers.ValidationError("短信验证码错误")

             #4,通过手机号查询美多用户是否存在,判断密码正确性
             user = None
             try:
                 user = User.objects.get(mobile=mobile)
             except User.DoesNotExist:
                 pass
             else:
                 #5,表示用户存在,判断密码正确性
                 if not user.check_password(attrs["password"]):
                     raise serializers.ValidationError("密码错误")

             #6,返回校验之后的内容
             attrs["openid"] = openid
             attrs["user"] = user
             return attrs

         #重写create方法,创建qq用户
         def create(self, validated_data):
             """validated_data,就上面返回的attrs"""
             #1,创建qq用户
             oauth_qq = OAuthQQUser()

             #2,判断用户是否存在,如果存在设置属性,如果不存在直接创建
             user = validated_data["user"]
             if not user:
                 user = User.objects.create(
                     username=validated_data["mobile"],
                     mobile=validated_data["mobile"],
                )
                 user.set_password(validated_data["password"])
                 user.save()

             #3,设置qq用户属性
             oauth_qq.openid = validated_data["openid"]
             oauth_qq.user = user
             oauth_qq.save()

             #4,返回
             return oauth_qq
    • oauth/utils.py 中准备序列化 OpenID 的工具方法

      # 对openid解密
      def check_save_user_openid(access_token):
         #1,创建serializer对象
         serializer = TJWSSerializer(settings.SECRET_KEY,expires_in=300)

         #2,解密openid
         dict_data = serializer.loads(access_token)

         #3,返回
         return dict_data.get("openid")

      以上引用:https://www.jianshu.com/p/8d46ff12baf7

  • 相关阅读:
    Centos 5.5 Lamp源码包安装编译 新风宇宙
    Linux系统日志管理 新风宇宙
    ubuntu设置时区,网上同步时间 新风宇宙
    ubuntu vim输入中文设置(SecureCRT下) 新风宇宙
    DIV+CSS容易犯的十个错误 新风宇宙
    apache性能优化 新风宇宙
    java里面main函数为什么要用static修饰
    如何设计mysql数据库和数据表
    PHP 图片验证码
    PHP免费空间选择方法概述
  • 原文地址:https://www.cnblogs.com/yblackd/p/11837477.html
Copyright © 2020-2023  润新知