客戶端 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字段記錄的登陸前用戶希望訪問的頁面。