1. 成為QQ互聯的開發者 參考鏈接:
<http://wiki.connect.qq.com/%E6%88%90%E4%B8%BA%E5%BC%80%E5%8F%91%E8%80%85>
2. 審核通過后,創建應用,即獲取本項目對應與QQ互聯的應用ID 參考鏈接:http://wiki.connect.qq.com/__trashed-2
3. 在 models.py 中定義QQ身份(openid)與用戶模型類User的關聯關系
class OAuthQQUser(models.Model):
"""
QQ登錄用戶數據
"""
user = models.ForeignKey('users.User', on_delete=models.CASCADE, verbose_name='用戶')
openid = models.CharField(max_length=64, verbose_name='openid', db_index=True)
create_time = models.DateTimeField(auto_now_add=True, verbose_name="創建時間")
update_time = models.DateTimeField(auto_now=True, verbose_name="更新時間")
class Meta:
db_table = 'tb_oauth_qq'
verbose_name = 'QQ登錄用戶數據'
verbose_name_plural = verbose_name # 單復數同名
4. QQ登錄SDK使用
初始化OAuthQQ對象
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET,
redirect_uri=settings.QQ_REDIRECT_URI, state=next)
獲取QQ登錄掃碼頁面,掃碼后得到Authorization Code
login_url = oauth.get_qq_url()
通過Authorization Code獲取Access Token
access_token = oauth.get_access_token(code)
通過Access Token獲取OpenID
openid = oauth.get_open_id(access_token)
實現流程

具體流程
-
返回QQ登錄網址的視圖
1.1 在配置文件中添加關於QQ登錄的應用開發信息
# QQ登錄參數
QQ_CLIENT_ID = 'xxxx' # ID
QQ_CLIENT_SECRET = 'xxxxxxxxxxxx' # 密鑰
QQ_REDIRECT_URI = 'http://www.xxxx.xxx/oauth_callback.html' # 回調域1.2 接口設計
-
請求方式:GET /oauth/qq/statues/?state=xxx
-
請求參數:查詢字符串參數
參數名 類型 是否必須 說明 state str 否 用戶QQ登錄成功后進入的網址 -
返回數據:JSON
{"login_url": oauth.get_qq_url()}返回值 類型 是否必須 說明 login_url str 是 qq登錄網址
1.3 邏輯實現
class QQAuthURLView(APIView):
"""
提供QQ登錄頁面網址
"""
def get(self, request):
# state表示從哪個頁面進入到的登錄頁面,將來登錄成功后,就自動回到那個頁面
state= request.query_params.get('state')
if not state:
state= '/'
# 獲取QQ登錄頁面網址
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET, redirect_uri=settings.QQ_REDIRECT_URI, state=state)
login_url = oauth.get_qq_url()
return Response({'login_url': login_url}) -
-
OAuth認證
准備oauth_callback回調頁,用於掃碼后接受Authorization Code
通過Authorization Code獲取Access Token
通過Access Token獲取OpenID
2.1 接口設計
-
請求方式:GET /oauth/qq/users/?code=xxx
-
請求參數: 查詢字符串參數
參數名 類型 是否必須 說明 code str 是 qq返回的授權憑證code -
返回數據
{"access_token": xxxx,}
或
{
"token": "xxx",
"username": "python",
"user_id": 1
}返回值 類型 是否必須 說明 access_token str 否 用戶是第一次使用QQ登錄時返回,其中包含openid,用於綁定身份使用,注意這個是我們自己生成的 token str 否 用戶不是第一次使用QQ登錄時返回,登錄成功的JWT token username str 否 用戶不是第一次使用QQ登錄時返回,用戶名 user_id int 否 用戶不是第一次使用QQ登錄時返回,用戶id
2.2 邏輯實現
oauth/views.py
class QQAuthUserView(GenericAPIView):
"""用戶掃碼登錄的回調處理"""
# 指定序列化器
serializer_class = serializers.QQAuthUserSerializer
def get(self, request):
# 提取code請求參數
code = request.query_params.get('code')
if not code:
return Response({'message':'缺少code'},
status=status.HTTP_400_BAD_REQUEST)
# 創建工具對象
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,
client_secret=settings.QQ_CLIENT_SECRET,
redirect_uri=settings.QQ_REDIRECT_URI)
try:
access_token = oauth.get_access_token(code)
# 3,通過access_token獲取openid
openid = oauth.get_open_id(access_token)
# 4,通過openid查詢oauthqq對象
try:
oauth_qq_user = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
# ①, 沒有項目用戶, 也沒有OAuthQQUser用戶
# ②, 有項目用戶, 沒有OAuthQQUser用戶
# 5,qq用戶沒有和項目用戶綁定過,加密openid,並返回
access_token_openid = generate_save_user_openid(openid)
return Response({"access_token": access_token_openid})
except Exception:
return Response({"message": "請求qq服務器異常"},
status=status.HTTP_400_BAD_REQUEST)
# 6,oauth_qq_user存在,並且綁定過了美多用戶
user = oauth_qq_user.user
# 7,組織數據,拼接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({
"user_id": user.id,
"username": user.username,
"token": token
})在 oauth/utils.py** 中准備序列化 OpenID 的工具方法**
# 對openid加密
def generate_save_user_openid(openid):
#1,創建TJWSSerializer對象
serializer = TJWSSerializer(settings.SECRET_KEY,expires_in=300)
#2,加密數據
token = serializer.dumps({"openid":openid})
#3,返回
return token -
-
OpenID綁定用戶
如果用戶是首次使用QQ登錄,則需要綁定用戶

3.1 接口設計
-
請求方式:POST /oauth/qq/users/
-
請求參數:JSON 或 表單
參數名 類型 是否必須 說明 mobile str 是 手機號 password str 是 密碼 sms_code str 是 短信驗證碼 access_token str 是 憑據(包含openid) -
返回數據:JSON
返回值 類型 是否必須 說明 token str 是 JWT token id int 是 用戶id username str 是 用戶名
3.2 邏輯實現
-
oauth/views.py
def post(self, request):
# 1,獲取數據
dict_data = request.data
# 2,獲取序列化器,校驗數據
serializer = self.get_serializer(data=dict_data)
serializer.is_valid(raise_exception=True)
# 3,數據入庫
oauth_qq = serializer.save()
# 4,組織,數據返回響應
user = oauth_qq.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({
"user_id": user.id,
"username": user.username,
"token": token
}) -
新建 oauth/serializers.py 文件
-
from rest_framework import serializers
from .utils import check_save_user_openid
from django_redis import get_redis_connection
from users.models import User
from .models import OAuthQQUser
class QQAuthUserSerializer(serializers.Serializer):
mobile = serializers.RegexField(label="手機號",regex=r"1[3-9]\d{9}")
password = serializers.CharField(label="密碼",min_length=8,max_length=20)
sms_code = serializers.CharField(label="短信",min_length=6,max_length=6)
access_token = serializers.CharField(label="token",min_length=1)
def validate(self, attrs):
"""多字段校驗"""
#1,獲取加密的openid
access_token = attrs["access_token"]
#2,調用方法解密openid,判斷是否存在
openid = check_save_user_openid(access_token)
if not openid:
raise serializers.ValidationError("openid失效")
#3,獲取redis中的短信,判斷為空,正確性
sms_code = attrs["sms_code"]
mobile = attrs["mobile"]
redis_conn = get_redis_connection("code")
redis_sms_code = redis_conn.get("sms_%s"%mobile)
if not redis_sms_code:
raise serializers.ValidationError("短信驗證碼過期")
if sms_code != redis_sms_code.decode():
raise serializers.ValidationError("短信驗證碼錯誤")
#4,通過手機號查詢美多用戶是否存在,判斷密碼正確性
user = None
try:
user = User.objects.get(mobile=mobile)
except User.DoesNotExist:
pass
else:
#5,表示用戶存在,判斷密碼正確性
if not user.check_password(attrs["password"]):
raise serializers.ValidationError("密碼錯誤")
#6,返回校驗之后的內容
attrs["openid"] = openid
attrs["user"] = user
return attrs
#重寫create方法,創建qq用戶
def create(self, validated_data):
"""validated_data,就上面返回的attrs"""
#1,創建qq用戶
oauth_qq = OAuthQQUser()
#2,判斷用戶是否存在,如果存在設置屬性,如果不存在直接創建
user = validated_data["user"]
if not user:
user = User.objects.create(
username=validated_data["mobile"],
mobile=validated_data["mobile"],
)
user.set_password(
