网站第三方登陆介绍(QQ互联)

客户端                                                              web server                                                                     QQ互联

 

可直接使用QQ登录工具QQLoginTool

pip install QQLoginTool

1. 获取QQ登陆扫码页面

class QQAuthURLView(View):
    """提供QQ登陆扫码页面"""

    def get(self, request):
        # 接收next
        next = request.GET.get('next')
        # 创建工具对象
        oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET,
                        redirect_uri=settings.QQ_REDIRECT_URI,
                        state=next)
        # 生成QQ登陆扫码连接地址
        login_url = oauth.get_qq_url()

        # 响应
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'login_url': login_url})

2. 接收Authorization Code,OAuth2.0认证获取openid

class QQAuthUserView(View):
    """处理QQ登陆回调:oauth_callback"""

    def get(self, request):
        """处理QQ回调的业务逻辑"""

        # 获取code
        code = request.GET.get('code')
        if not code:
            return http.HttpResponseForbidden('获取code失败')

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

        try:
            # 使用code获取access_token
            access_token = oauth.get_access_token(code)

            # 使用access_token获取openid
            openid = oauth.get_open_id(access_token)

        except Exception as e:
            logger.error(e)
            return http.HttpResponseServerError('OAuth2.0认证失败')
        # 使用openid判断该QQ用户是否绑定过商城的用户
        try:
            oauth_user = OAuthQQUser.objects.get(openid=openid)
        except OAuthQQUser.DoesNotExist:
            # openid未绑定商城用户
            access_token_openid = generate_access_token(openid)
            context = {'access_token_openid': access_token_openid}
            return render(request, 'oauth_callback.html', context)
        else:
            # openid已绑定商城用户,oauth_user.user表示从QQ登陆模型类对象中找到对应的用户模型类对象
            login(request, oauth_user.user)

            # 重定向到state:从哪来,QQ登录完之后回哪儿去
            next = request.GET.get('state')
            response = redirect(next)

            # 将用户名写入到cookie中
            response.set_cookie('username', oauth_user.user.username, max_age=3600)

            # 响应QQ登陆结果
            return response

    def post(self, request):
        """商城用户绑定到openid"""
        # 接收参数
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        sms_code_client = request.POST.get('sms_code')
        access_token_openid = request.POST.get('access_token_openid')

        # 校验参数
        # 判断参数是否齐全
        if not all([mobile, password, sms_code_client, access_token_openid]):
            return http.HttpResponseForbidden('缺少必传参数')
        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('请输入正确的手机号')
        # 判断密码是否合格
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.HttpResponseForbidden('请输入8-20位的密码')
        # 判断短信验证码是否一致
        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', {'error_sms_code': '无效的短信验证码'})
        if sms_code_server.decode() != sms_code_client:
            return render(request, 'oauth_callback.html', {'error_sms_code': '输入短信验证码有误'})
        # 判断openid是否有效
        # openid使用itsdangerous签名之后只有600秒的有效期
        openid = check_access_token(access_token_openid)
        if not openid:
            return render(request, 'oauth_callback.html', {'openid_errmsg': 'openid已失效'})

        # 使用手机号查询对应的用户是否存在
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            # 如果手机号用户不存在,新建用户
            user = User.objects.create_user(username=mobile, password=password, mobile=mobile)
        else:
            # 如果手机号用户存在,需要校验密码
            if not user.check_password(password):
                return render(request, 'oauth_callback.html', {'account_errmsg': '账号或密码错误'})

        # 将新建及已存在用户绑定到openid
        #     oauth_qq_user = OAuthQQUser(user=user, openid=openid)
        #     oauth_qq_user.save()
        try:
            oauth_qq_user = OAuthQQUser.objects.create(user=user, openid=openid)
        except Exception as e:
            logger.error(e)
            return render(request, 'oauth_callback.html', {'qq_login_errmsg': 'QQ登录失败'})
        # 实现状态保持
        login(request, user)
        # 重定向到state
        next = request.GET.get('state')
        response = redirect(next)

        # 将用户名写入到cookie中
        response.set_cookie('username', oauth_qq_user.user.username, max_age=3600)

        # 响应QQ登陆结果
        return response

3. 演示

(1) 前端用户点击QQ登陆按钮,生成了login_url

(2) 用户进入扫码页面,扫码后:

由于此时用户QQ账号未与商城内用户绑定,因此定义序列化方法将openid以隐藏标签的形式存储在前端页面

def generate_access_token(openid):
    """
    签名、序列化openid
    :param openid: openid明文
    :return: token(openid密文)
    """
    # 创建序列化器对象
    # s = Serializer('密钥: 越复杂越安全', '过期时间')
    s = Serializer(settings.SECRET_KEY, constants.ACCESS_TOKEN_EXPIRES)

    # 准备待序列化的字典数据
    data = {'openid': openid}

    # 调用dumps方法进行序列化: 类型是bytes
    token = s.dumps(data)

    # 返回序列化后的数据
    return token.decode()
<input type="hidden" name="access_token_openid" value="eyJhbGciOiJIUzUxMiIsImlhdCI6MTU4MzQxNDcwNywiZXhwIjoxNTgzNDE1MzA3fQ.eyJvcGVuaWQiOiI1ODU0MDQ3Q0Q3MjhGOEQ2N0IyMERERTdDMDhCOTcwRiJ9.OkbAjloC01h3oH3YmmbW4lhwR4oCHYqCFQdY7DM-5jH0WggxUQgkQkZoLV-U_Bo7pz0BJ8PzrOQb1wKYEHN4JA">

(3) 用户开始绑定QQ账号和商城内账号

当用户点击绑定时,后端将前端暂存的加密后的openid再次解密。

并将openid作为字段存储在QQ用户认证库中。

随后将重定向到next字段记录的登陆前用户希望访问的页面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WUYANGEZRA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值