美文网首页
2018-07-06(第三方登陆(QQ))

2018-07-06(第三方登陆(QQ))

作者: 江帆233 | 来源:发表于2018-07-06 17:22 被阅读0次

    第三方登陆(QQ)


    QQ互联开放平台为第三方网站提供了丰富的API。第三方网站接入QQ互联开放平台后,即可通过调用平台提供的API实现用户使用QQ帐号登录网站功能,且可以获取到腾讯QQ用户的相关信息。--QQ互联

    登陆流程

    1、在需要登陆的地方放置QQ登陆按钮

    这个在QQ互联提供的文档中获取

    2、点击QQ登陆按钮发送请求获取授权页面

    前端按钮绑定的JS代码:

    // qq登录
        qq_login: function(){
            var next = this.get_query_string('next') || '/';
            axios.get(this.host + '/oauth/qq/authorization/?next=' + next, {
                    responseType: 'json'
                })
                .then(response => {
                    location.href = response.data.login_url;
                })
                .catch(error => {
                    console.log(error.response.data);
                })
        }
    

    host的地址为后端api域名

    后端视图 GET /oauth/qq/authorization//?next=xxx(加next是为了登陆成功后获取以next中的地址跳转至特定页面,后面会用到):

    添加辅助类:

    class OAuthQQ(object):
    # 对openid进行加解密的安全密钥
    SECRET_KEY = settings.SECRET_KEY
    # 对openid加密之后生成的access_token的有效时间
    EXPIRES_IN = 10 * 60
    
    def __init__(self, client_id=None, client_secret=None, redirect_uri=None, state=None):
        # QQ网站应用客户端id
        self.client_id = client_id or settings.QQ_CLIENT_ID
        # self.client_id = client_id if client_id else settings.QQ_CLIENT_ID
        # QQ网站应用客户端安全密钥
        self.client_secret = client_secret or settings.QQ_CLIENT_SECRET
        # 网站回调url网址
        self.redirect_uri = redirect_uri or settings.QQ_REDIRECT_URI
        self.state = state or settings.QQ_STATE
    
    def get_login_url(self):
        """
        获取QQ的登录网址:
        """
        # 组织参数
        params = {
            'response_type': 'code',
            'client_id': self.client_id,
            'redirect_uri': self.redirect_uri,
            'state': self.state,
            'scope': 'get_user_info'
        }
    
        # 拼接url地址
        url = 'https://graph.qq.com/oauth2.0/authorize?' + urlencode(params)
    
        return url
    

    这个类可以拼接符合文档要求的url地址,调用类中的get_login_url方法可返回地址,setting在配置项中配置。

    后端视图:

    # GET /oauth/qq/authorization/?next=xxx
    class QQAuthURLView(APIView):
    """
    QQ登录的网址:
    """
    def get(self, request):
        next = request.query_params.get('next', '/')
    
    # 获取QQ登录地址,OAuthQQ为上面类
    oauth = OAuthQQ(state=next)
    login_url = oauth.get_login_url()
    
    # 返回QQ登录地址
    return Response({'login_url': login_url})
    

    后端视图调用后前端可接收地址跳转至认证页面。

    3、认证通过后,将跳转至回调页面,并在redirect_uri地址后带上Authorization Code和原始的state值。如:PC网站:http://graph.qq.com/demo/index.jsp?code=9A5F************************06AF&state=test。此时在页面对应的前端回调页面js添加如下代码:

    mounted: function(){
        // 从路径中获取qq重定向返回的code
        var code = this.get_query_string('code');
        axios.get(this.host + '/oauth/qq/user/?code=' + code, {
                responseType: 'json',
            })
            .then(response => {
                if (response.data.user_id){
                    // 用户已绑定
                    sessionStorage.clear();
                    localStorage.clear();
                    localStorage.user_id = response.data.user_id;
                    localStorage.username = response.data.username;
                    localStorage.token = response.data.token;
                    var state = this.get_query_string('state');
                    location.href = state;
                } else {
                    // 用户未绑定
                    this.access_token = response.data.access_token;
                    this.generate_image_code();
                    this.is_show_waiting = false;
                }
            })
            .catch(error => {
                console.log(error.response.data);
                alert('服务器异常');
            })
    },
    

    回调页面会发送请求至API GET /oauth/qq/user/?code=xxx
    在辅助类中继续添加如下方法:

    def get_access_token(self, code):
    """
    获取到code后拼接地址得到授权令牌,Access_Token
    """
    # 组织参数
    params = {
        'grant_type': 'authorization_code',
        'client_id': self.client_id,
        'client_secret': self.client_secret,
        'code': code,
        'redirect_uri': self.redirect_uri,
    }
    
    # 拼接url地址
    url = 'https://graph.qq.com/oauth2.0/token?' + urlencode(params)
    try:
        # 访问获取accesss_token
        response = urlopen(url)
    except Exception as e:
        raise QQAPIError(str(e))
    
    # 返回数据格式如下:
    # access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14
    # 获取响应数据并解码
    res_data = response.read().decode()
    # 转化成字典
    res_dict = parse_qs(res_data)
    
    # 尝试从字典中获取access_token
    access_token = res_dict.get('access_token')
    
    if not access_token:
        # 获取access_token失败
        raise QQAPIError(res_dict)
    
    # 返回access_token
    return access_token[0]
    
    def get_openid(self, access_token):
        """
        获取QQ授权用户的openid:
        access_token: QQ返回的access_token
        """
        # 拼接url地址
        url = 'https://graph.qq.com/oauth2.0/me?access_token=' + access_token
    
        try:
            # 访问获取QQ授权用户的openid
            response = urlopen(url)
        except Exception as e:
            raise QQAPIError(str(e))
    
        # 返回数据格式如下:
        # callback({"client_id": "YOUR_APPID", "openid": "YOUR_OPENID"});\n
        res_data = response.read().decode()
        try:
            res_dict = json.loads(res_data[10:-4])
        except Exception as e:
            res_dict = parse_qs(res_data)
            raise QQAPIError(res_dict)
    
        # 获取openid
        openid = res_dict.get('openid')
        return openid
    
    @classmethod
    def generate_save_user_token(cls, openid, secret_key=None, expires=None):
        """
        对openid进行加密:
        openid: QQ授权用户的openid
        secret_key: 密钥
        expires: token有效时间
        """
        if secret_key is None:
           secret_key = cls.SECRET_KEY
    
        if expires is None:
           expires = cls.EXPIRES_IN
    
        serializer = TJWSSerializer(secret_key, expires)
    
        token = serializer.dumps({'openid': openid})
        return token.decode()
    

    itsdangerous模块的使用:

    1、导入模块

    from itsdangerous import TimedJSONWebSignatureSerializer

    2、创建对象

    serializer = TimedJSONWebSignatureSerializer(secret_key=secret_key密码, expire_time解密的有效时间))

    3、加密数据,返回bytes类型

    res_data=serializer.dumps(要加密的数据)

    4、解密数据

    res_data_=serializer.loads(res_data)

    注意:加密和解密时需要密码一致,否则无法解密。

    GET /oauth/qq/user/?code=xxx对应的视图:

    class QQAuthUserView(APIView):
    def get(self, request):
        # 1. 获取QQ返回的code
        code = request.query_params.get('code')
    
        try:
            # 2. 根据code获取access_token
            oauth = OAuthQQ()
            access_token = oauth.get_access_token(code)
            # 3. 根据access_token获取授权QQ用户的openid
            openid = oauth.get_openid(access_token)
        except QQAPIError as e:
            logger.error(e)
            return Response({'message': 'QQ服务异常'}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
    
        # 4. 根据`openid`查询tb_oatu_qq表,判断是否已经绑定账号
        try:
            oauth_user = OAuthQQUser.objects.get(openid=openid)
        except OAuthQQUser.DoesNotExist:
            # 4.2 如果未绑定,返回token
            token = oauth.generate_save_user_token(openid)
            return Response({'access_token': token})
    
        else:
            # 4.1 如果已经绑定,生成JWT token信息
            # 补充生成记录登录状态的token
            user = oauth_user.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)
    
            response = Response({
                'token': token,
                'user_id': user.id,
                'username': user.username
            })
            return response 
    

    类视图QQAuthUserView调用辅助类的方法获取到OpenID,此时会查询数据库,发现已经有表中存在绑定OpenID的对象,说明此用户已经绑定qq号,签发JWT,返回用户信息,前端收到Response,会跳转至next中的回调页面;如果没有查询到,则加密返回OpenID,显示绑定账号标签。

    下面即对账号的验证并绑定OpenID,OpenID设置了过期时间,时间过长即失效。。。

    相关文章

      网友评论

          本文标题:2018-07-06(第三方登陆(QQ))

          本文链接:https://www.haomeiwen.com/subject/elebuftx.html