绑定用户身份接口

如果用户是首次使用QQ登录,则需要绑定用户,页面如下:

QQ登录绑定用户

业务逻辑:

  • 用户需要填写手机号、密码、图片验证码、短信验证码
  • 如果用户未在美多商城注册过,则会将手机号作为用户名为用户创建一个美多账户,并绑定用户
  • 如果用户已在美多商城注册过,则检验密码后直接绑定用户

绑定QQ身份的处理流程

绑定QQ身份上

绑定QQ身份下

后端接口设计

请求方式: POST /oauth/qq/user/

请求参数: JSON 或 表单

参数 类型 是否必须 说明
mobile str 手机号
password str 密码
sms_code str 短信验证码
access_token str 凭据 (包含openid)

返回数据: JSON

返回值 类型 是否必须 说明
token str JWT token
id int 用户id
username str 用户名

在OAuthQQ辅助类中增加

    @classmethod
    def check_save_user_token(cls, token, secret_key=None, expires=None):
        """
        对token进行校验:
        token: 对openid加密之后生成的token
        secret_key: 密钥
        expires: token有效时间
        """
        if secret_key is None:
            secret_key = cls.SECRET_KEY

        if expires is None:
            expires = cls.EXPIRES_IN

        serializer = TJWSSerializer(secret_key, expires)

        try:
            data = serializer.loads(token)
        except BadData:
            return None
        else:
            return data.get('openid')

新建oauth/serializers.py文件,

class OAuthQQUserSerializer(serializers.ModelSerializer):
    """
    保存QQ用户序列化器
    """
    sms_code = serializers.CharField(label='短信验证码', write_only=True)
    access_token = serializers.CharField(label='操作凭证', write_only=True)
    token = serializers.CharField(read_only=True)
    mobile = serializers.RegexField(label='手机号', regex=r'^1[3-9]\d{9}$')

    class Meta:
        model = User
        fields = ('mobile', 'password', 'sms_code', 'access_token', 'id', 'username', 'token')
        extra_kwargs = {
            'username': {
                'read_only': True
            },
            'password': {
                'write_only': True,
                'min_length': 8,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许8-20个字符的密码',
                    'max_length': '仅允许8-20个字符的密码',
                }
            }
        }

    def validate(self, attrs):
        # 检验access_token
        access_token = attrs['access_token']

        openid = OAuthQQ.check_save_user_token(access_token)
        if not openid:
            raise serializers.ValidationError('无效的access_token')

        attrs['openid'] = openid

        # 检验短信验证码
        mobile = attrs['mobile']
        sms_code = attrs['sms_code']
        redis_conn = get_redis_connection('verify_codes')
        real_sms_code = redis_conn.get('sms_%s' % mobile)
        if real_sms_code.decode() != sms_code:
            raise serializers.ValidationError('短信验证码错误')

        # 如果用户存在,检查用户密码
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            pass
        else:
            password = attrs['password']
            if not user.check_password(password):
                raise serializers.ValidationError('密码错误')
            attrs['user'] = user
        return attrs

    def create(self, validated_data):
        openid = validated_data['openid']
        user = validated_data.get('user')
        mobile = validated_data['mobile']
        password = validated_data['password']

        if not user:
            # 如果用户不存在,创建用户,绑定openid(创建了OAuthQQUser数据)
            user = User.objects.create_user(username=mobile, mobile=mobile, password=password)

        OAuthQQUser.objects.create(user=user, openid=openid)

        # 签发jwt 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)

        user.token = token

        return user

在oauth/views.py修改QQAuthUserView 视图

class QQAuthUserView(CreateAPIView):
    """
    QQ登录的用户
    """
    serializer_class = OAuthQQUserSerializer

    def get(self, request):
        ...

前端

在oauth_back.js中实现

        // 保存
        on_submit: function(){
            this.check_pwd();
            this.check_phone();
            this.check_sms_code();

            if(this.error_password == false && this.error_phone == false && this.error_sms_code == false) {
                axios.post(this.host + '/oauth/qq/user/', {
                        password: this.password,
                        mobile: this.mobile,
                        sms_code: this.sms_code,
                        access_token: this.access_token
                    }, {
                        responseType: 'json',
                    })
                    .then(response => {
                        // 记录用户登录状态
                        sessionStorage.clear();
                        localStorage.clear();
                        localStorage.token = response.data.token;
                        localStorage.user_id = response.data.id;
                        localStorage.username = response.data.username;
                        location.href = this.get_query_string('state');
                    })
                    .catch(error=> {
                        if (error.response.status == 400) {
                            this.error_sms_code_message = error.response.data.message;
                            this.error_sms_code = true;
                        } else {
                            console.log(error.response.data);
                        }
                    })
            }
        }