网站实现QQ登录功能-Python示例

qqlogin.png

为站点增加QQ登录的支持,可以让访客更方便的称谓站点的用户,避免了单独注册账号的繁琐,也避免了忘记站点密码的烦恼。

QQ连接采用的是OAuth2.0的方式授权,所以一个用于请求的RestClient类是必不可少的,这里提供一个自己写的简易Python版,封装了GET/POST/PUT/DELETE四种操作,PUT和DELETE还未测试。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urllib
import urllib2


class RestClient(object):

    def __init__(self, baseurl):
        self.baseurl = baseurl

    def GET(self, apiurl, params):
        querystring = []
        for key in params.keys():
            querystring.append('%s=%s' % (key, params[key]))
        querystring = '?' + '&'.join(querystring)
        fullapipath = self.baseurl + apiurl + querystring
        req = urllib2.Request(fullapipath)
        res_data = urllib2.urlopen(req)
        res = res_data.read()
        return res

    def POST(self, apiurl, params):
        fullapipath = self.baseurl + apiurl
        params_urlencode = urllib.urlencode(params)
        req = urllib2.Request(fullapipath, params_urlencode)
        res_data = urllib2.urlopen(req)
        res = res_data.read()
        return res

    def PUT(self, apiurl, params):
        fullapipath = self.baseurl + apiurl
        params_urlencode = urllib.urlencode(params)
        req = urllib2.Request(fullapipath, params_urlencode)
        req.get_method = lambda: 'PUT'
        res_data = urllib2.urlopen(req)
        res = res_data.read()
        return res

    def DELETE(self, apiurl):
        fullapipath = self.baseurl + apiurl
        req = urllib2.Request(fullapipath)
        req.get_method = lambda: 'DELETE'
        res_data = urllib2.urlopen(req)
        res = res_data.read()
        return res

有了RestClient类后,创建一个用于访问QQ互联的RestClient类,所以我们命名为QQConnectClient,并让他继承RestClient类,在此基础上根据QQ互联的API封装了一些常用的基础方法,详情参见注释内容

class QQConnectClient(RestClient):

    def __init__(self, baseurl, app_id, app_secret, access_token=None):
        super(QQConnectClient, self).__init__(baseurl)
        self.app_id = app_id
        self.app_secret = app_secret
        self.access_token = access_token

    def get_access_token(self, code, redirect_uri):
        """
            通过Authorization Code获取Access Token
        """
        params = {}
        params['grant_type'] = 'authorization_code'
        params['client_id'] = self.app_id
        params['client_secret'] = self.app_secret
        params['code'] = code
        params['redirect_uri'] = redirect_uri
        data = self.GET('/oauth2.0/token', params)
        data_mapping = self.__cast2dict(data)
        self.access_token = data_mapping['access_token']
        self.expires_in = data_mapping['expires_in']
        self.refresh_token = data_mapping['refresh_token']
        return self.access_token

    def refresh_access_token(self):
        """
            刷新Access Token
        """
        params = {}
        params['grant_type'] = 'refresh_token'
        params['client_id'] = self.app_id
        params['client_secret'] = self.app_secret
        params['refresh_token'] = self.refresh_token
        data = self.GET('/oauth2.0/token', params)
        data_mapping = self.__cast2dict(data)
        self.access_token = data_mapping['access_token']
        self.expires_in = data_mapping['expires_in']
        self.refresh_token = data_mapping['refresh_token']

    def get_openid(self):
        """
            根据Access token获取用户的OpenId
        """
        params = {}
        params['access_token'] = self.access_token
        return self.__cast2dict(self.GET('/oauth2.0/me', params))

    def get_user_info(self, openid):
        """
            获取用户在QQ空间的个人资料(仅支持网站调用)
        """
        params = {}
        params['openid'] = openid
        params['access_token'] = self.access_token
        params['oauth_consumer_key'] = self.app_id
        params['format'] = 'json'
        return self.__cast2dict(self.GET('/user/get_user_info', params))

    def __cast2dict(self, data):
        try:
            return json.loads(data)
        except ValueError:
            if data.find('callback') >= 0:
                return json.loads(data[data.find('{'):data.rfind('}') + 1])
            else:
                data_mapping = {}
                for kv in data.split('&'):
                    kv_array = kv.split('=')
                    data_mapping[kv_array[0]] = kv_array[1]
                return data_mapping

万事俱备只欠东风,接下来需要将QQ的回调地址的action实现,根据回调传来的code和state参数,以及应用的appid和appsecret来换取Access Token从而访问各种QQ互联的API操作,例如获取openid来绑定本地用户,从而实现支持QQ登录。

def GET(self):
        i = web.input(code='', state='')
        code = i.code
        state = i.state
        client = QQConnectClient(config.OAuth_QQ_baseurl,
                                 config.OAuth_QQ_appid,
                                 config.OAuth_QQ_appsecret)
        client.get_access_token(code, config.OAuth_QQ_callback)
        openid = client.get_openid()
        ...


  • 站长:Dreamheart_Zhao
  • 职业:专职程序猿
  • 技能:
    • Python
    • C#
    • .NetMVC
    • C++
    • Java
  • Email: addonce@163.com