• 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'  # IDQQ_CLIENT_SECRET = 'xxxxxxxxxxxx'  # 密钥QQ_REDIRECT_URI = 'http://www.xxxx.xxx/oauth_callback.html'  # 回调域
      

      1.2 接口设计

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

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

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

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

      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

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

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

        {"access_token": xxxx,}或{    "token": "xxx",    "username": "python",    "user_id": 1}
        
        返回值类型是否必须说明
        access_tokenstr用户是第一次使用QQ登录时返回,其中包含openid,用于绑定身份使用,注意这个是我们自己生成的
        tokenstr用户不是第一次使用QQ登录时返回,登录成功的JWT token
        usernamestr用户不是第一次使用QQ登录时返回,用户名
        user_idint用户不是第一次使用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 或 表单

      参数名类型是否必须说明
      mobilestr手机号
      passwordstr密码
      sms_codestr短信验证码
      access_tokenstr凭据(包含openid)
    • 返回数据:JSON

      返回值类型是否必须说明
      tokenstrJWT token
      idint用户id
      usernamestr用户名

    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 serializersfrom .utils import check_save_user_openidfrom django_redis import get_redis_connectionfrom users.models import Userfrom .models import OAuthQQUserclass 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"],           )
  • 相关阅读:
    automake搭建项目工程
    Realtek SDK缓冲区溢出和命令执行漏洞说明
    阿里云负载均衡(SLB)简介
    解决尚医通com.aliyun.oss 和com.aliyun 爆红
    栈的简单实现及应用
    Java 断点下载(下载续传)服务端及客户端(Android)代码
    Unity开发之观察者模式(事件中心)
    Linux系统下rar软件的安装以及如何解压文件
    vue-swiper组件化:解决异步请求数据时swiper过早初始化问题:
    Touch命令使用指南:创建、更新和修改文件时间戳
  • 原文地址:https://blog.csdn.net/weixin_44070137/article/details/127736147