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' # IDQQ_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登录,则需要绑定用户
-
请求方式: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 serializersfrom .utils import check_save_user_openidfrom django_redis import get_redis_connectionfrom users.models import Userfrom .models import OAuthQQUserclass 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"], )