1. 程式人生 > >QQ第三方登入

QQ第三方登入

QQ登入

QQ登入,亦即我們所說的第三方登入,是指使用者可以不在本專案中輸入密碼,而直接通過第三方的驗證,成功登入本專案。

使用QQ登入的流程

QQ登入的大致流程為:

1.使用者在瀏覽器點選QQ登入按鈕,向後端傳送請求,後端伺服器根據使用者傳來的查詢字串中?next=xxxx(用於指定登入後跳轉的頁面,如果使用者是通過點選個人中心來到登入頁面,則登入成功後跳回使用者中心),生成QQ登入頁面的url並返回。

params = {

'response_type':'code',

'client_id':self.client_id,

'client_secret'

:self.client_secret,

'redirect_uri':self.redirect_uri,

'state':self.state,

'scope':'get_user_info',

}

url = 'https://graph.qq.com/oauth2.0/authorize?'+ urlencode(params)

return url

2.前端得到qq登入頁面的url,訪問qq伺服器,qq會將使用者重定向到伺服器的callback地址,並攜帶者code和state,state是第一步中的next,即登入後跳轉的頁面。

3.前端將向伺服器傳送請求,伺服器獲取code值後,向qq伺服器傳送請求,獲取access_token,然後帶者access_token再向qq伺服器傳送請求獲取openid。得到openid後,在資料庫中查詢,是否已經繫結過帳號,如果有則簽名並登入。如果沒有,則返回加密後的openid到使用者繫結頁面。前端傳送post請求將個人資訊傳送給伺服器,伺服器後端建立使用者(如果已有則需要驗證密碼是否正確)並繫結qq。

附:

#QQ登入的引數

QQ_CLIENT_ID = 'xxxxxxx'

QQ_CLIENT_SECRET = 'xxxxx'

QQ_REDIRECT_URI = 'http://xxxxx:8080/oauth_callback.html'

QQ_STATE = '/'

程式碼:

檢視:

class QQAuthURLView(APIView):

def get(self,request):

"""

提供用於qq登入的url

"""

next = request.query_params.get("next")

oauth = OAuthQQ(state=

next)

login_url = oauth.get_qq_login_url()

return Response({"login_url":login_url})

class QQAuthUserView(GenericAPIView):

"""

QQ登入的使用者

"""

serializer_class = OAuthQQUserSerializer

def get(self,request):

"""

獲取qq登入的使用者資料

"""

code = request.query_params.get('code')

if not code:

return Response({"message":"缺少code"},status=status.HTTP_400_BAD_REQUEST)

oauth = OAuthQQ()

#獲取openid

try:

access_token = oauth.get_access_token(code)[0]

openid = oauth.get_openid(access_token)

except Exception:

return Response({"message":"QQ伺服器錯誤"})

#判斷使用者是否存在

try:

qq_user = OAuthQQUser.objects.get(openid=openid)

except OAuthQQUser.DoesNotExist:

#使用者第一次登入

token = oauth.generate_sava_user_token(openid)

return Response({"access_token":token})

else:

#使用者不是第一次登入

user = qq_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)

return Response({

"token":token,

"user_id":user.id,

"username":user.username

})

def post(self,request):

serializer = self.get_serializer(data=request.data)

serializer.is_valid(raise_exception=True)

user = serializer.save()

# 生成已登入的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({

'token':token,

'user_id':user.id,

'username':user.username

})

工具類:

class OAuthQQ(object):

"""

QQ認證工具類

"""

def __init__(self,client_id=None, client_secret=None,redirect_uri=None,state=None):

self.client_id = client_id or settings.QQ_CLIENT_ID

self.client_secret = client_secret or settings.QQ_CLIENT_SECRET

self.redirect_uri = redirect_uri or settings.QQ_REDIRECT_URI

self.state = state or settings.QQ_STATE#登入成功後跳轉的頁面

def get_qq_login_url(self):

"""

獲取QQ登入的網址,構造引數返回前端

:return:

"""

params = {

'response_type':'code',

'client_id':self.client_id,

'client_secret':self.client_secret,

'redirect_uri':self.redirect_uri,

'state':self.state,

'scope':'get_user_info',

}

url = 'https://graph.qq.com/oauth2.0/authorize?'+ urlencode(params)

return url

def get_access_token(self,code):

        #獲取access_token

params = {

'grant_type':'authorization_code',

'client_id':self.client_id,

'client_secret':self.client_secret,

'redirect_uri':self.redirect_uri,

'code':code

}

#生成請求access_token的url

url = 'https://graph.qq.com/oauth2.0/token?'+urlencode(params)

#傳送請求

response = urlopen(url)

#返回來的值是二進位制,要進行解碼

response_data = response.read().decode()

#將查詢字串轉為字典

data = parse_qs(response_data)

#獲取access_token

access_token = data.get('access_token',None)

if access_token is None:

logger.error("code=%s msg=%s"%(data.get('code'), data.get('msg')))

raise QQAPIError

return access_token

def get_openid(self,access_token):

"""

獲取使用者的openid

:param access_token: qq提供的access_token

:return: open_id

"""

url = 'https://graph.qq.com/oauth2.0/me?access_token='+access_token

response = urlopen(url)

response_data = response.read().decode()

try:

# 返回的資料 callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} )\n;

data = json.loads(response_data[10:-4])

except Exception:

data = parse_qs(response_data)

logger.error("code=%s,msg=%s"%(data.get("code"),data.get('msg')))

raise QQAPIError

openid = data.get("openid")

return openid

@staticmethod

def generate_sava_user_token(openid):

"""

生成儲存使用者資料的token,加密

:param openid: 使用者的openid

:return: token

"""

serializer = Serializer(settings.SECRET_KEY, expires_in=constants.SAVE_QQ_USER_TOKEN_EXPIRES)

data = {

"openid":openid

}

token = serializer.dumps(data)

return token.decode()

@staticmethod

def check_save_user_token(token):

"""

檢驗儲存使用者資料的token

:param token: token

:return: openid or None

"""

serializer = Serializer(settings.SECRET_KEY, expires_in=constants.SAVE_QQ_USER_TOKEN_EXPIRES)

try:

data = serializer.loads(token)

except BadData:

return None

else:

return data.get("openid")