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)

实现流程

QQ第三方登录-python_web开发_django框架_第1张图片

具体流程

  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登录,则需要绑定用户

QQ第三方登录-python_web开发_django框架_第2张图片
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"],
              )

你可能感兴趣的:(python,django,前端,开发语言,后端)