- 第三方登录, QQ登录
# 先生成一个跳转地址
class QQAuthURLView(APIView): def get(self, request): next = request.query_params.get('state') if not next: next = '/' oauth = OAuthQQ(client_id=dev.QQ_CLIENT_ID,client_secret=dev.QQ_CLIENT_SECRET,redirect_uri=dev.QQ_REDIRECT_URI,state=next) login_url = oauth.get_qq_url() return Response({'login_url':login_url})
-
在跳转地址先生成一个code值发送给后端, 作用是判断是否已经绑定用户了
qq_login: function(){
var next = this.get_query_string(‘next’) || ‘/’;
axios.get(this.host + ‘/oauth/qq/authorization/?next=’ + next, {
responseType: ‘json’,
withCredentials: true
})
.then(response => {
location.href = response.data.login_url;
}) -
后端收到code值, 生成openid, 判断OAuthQQUser表中是否有该openid
- 如果有, 返回一个完整信息, 前端判断出传回来user_id后会直接跳转到state页面
from itsdangerous import TimedJSONWebSignatureSerializer as TJS
class QQUserView(CreateAPIView):
serializer_class = OauthSerializer
def get(self, request):
code=request.query_params.get(‘code’)
# 在这里传入state
qq=OAuthQQ(client_id=settings.QQ_CLIENT_ID,
client_secret=settings.QQ_CLIENT_SECRET,
redirect_uri=settings.QQ_REDIRECT_URI, state=’/’)
access_token=qq.get_access_token(code)
openid=qq.get_open_id(access_token)
try:
qq=OAuthQQ.objects.get(openid=openid)
except:
# 没有这个用户就要绑定操作了, 先把加密后的openid发到前端
tjs = TJS(settings.SECRET_KEY,300)
open_id = tjs.dumps({‘openid’:openid}).decode()
return Response({‘access_token’:open_id})
else:
# 返回数据要携带token值,user_id,username
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=qq.user
return Response({
‘token’: token,
‘user_id’: user.id,
‘username’: user.username
})
- 如果没有找到, 会返回加密后的openid, 前端显示绑定界面
access_token_openid = generate_save_user_token(openid)
return Response({‘access_token’:access_token_openid})
- 前端输入字段, 发送短信验证码后, 点击保存, 发送一个post请求. post请求作用是验证字段和保存数据.
CreateAPIView封装了post方法, 所以修改序列化器就可以了 class OauthSerializer(serializer.ModelSerializer): sms_code = serializer.CharField(max_length=6,min_length=6,write_only=True) token = serializers.CharField(read_only=True) mobile = serializers.CharField(max_lenght=11) access_token=serializers.CharField(write_only=True)
class Meta:
model=User
# 决定能在这个序列化器中来往的字段
fileds=('id','username','mobile','password',sms_code','token','access_token')
extra_kwargs={
'username':{
'read_only':True
},
'password':{
'max_length':20,
'min_length':8,
'write_only':True
},
}
def validate_mobile(self,value):
if not re.match(r'^1[3-9]\d{9}$',value):
raise seriallizers.ValidationError('手机格式不正确')
return value
def validate(self,attrs):
conn=get_reids_connection('sms_code')
rel_sms_code=conn.fet('sms_code_%s')%attrs['mobile']
if not rel_sms_code:
raise serializers.ValidationError('短信失效')
if attrs['sms_code'] != rel_sms_code:
raise serializers.ValidationError('短信错误')
tjs=TJS(settings.SECRET_KEY,300)
data=tjs.loads(attrs['access_token'])
openid=data.get('oopenid')
attrs['openid']=openid
try:
user=User.objects.get(mobile=attrs['mobile'])
except:
return attrs
else:
if not user.check_password(attrs['password']):
raise serializers.ValidationError('密码错误')
attrs['user']=user
return attrs
def create(self,attrs):
user=attrs.get('user')
if not user:
user=Usr.objects.create_user (username=attrs['username'],mobile=attrs['mobile'], password=attrs['password'])
# 保存数据, 创建了一个openid和user关联的对象
OAuthQQUser.objects.create(user=user,openid=attrs['openid'])
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
- 根据接口文档, 路由如下
urlpatterns = [
url(r’qq/authorization/ ′ , v i e w s . O a u t h Q Q V i e w . a s v i e w ( ) ) , u r l ( r ′ q q / u s e r / ',views.OauthQQView.as_view()), url(r'qq/user/ ′,views.OauthQQView.asview()),url(r′qq/user/’,views.QQUserView.as_view())
]
用户信息和邮箱验证
- 用户信息, 读取前端发送的token值, 取出user对象, 然后返回前端
class UserDetailView(RetrieveAPIView):
serializer_class = UserDetailSerialzier
permission_classes = [IsAuthenticated]
def get_object(self):
# 所有view的子类都有一个request属性
# 序列化返回user对象
return self.request.user
#序列化器如下, 决定返回的序列化数据
class UserDetailSerializer(serializer.ModelSerializer):
class Meta:
model=User
fields=('id','username','mobile','email','email_active')
extra_kwargs={
'username':{
'read_only':True
},
'mobile':{'read_only':True},
'email_acive':{'read_only':True}
}
def
-
用户邮箱修改, 视图和序列化器
class UserEmailView(UpdateAPIView):
serializer_class=UserDetailSerializer
# 因为没有传pk值, 所以也要重写获取对象的方法
def get_object(self):
return self.request.user# view调用了序列化器完成保存更新的操作 # 给序列化器增添update方法 def uptate(self,instance,attrs): instance.email=['email'] instance.save() # 加密手段 serializer = TJWSSerializer(settings.SECRET_KEY, expires_in=300) data={'user_id':instance.id,'email':instance.email} token=serializer.dumps(data).decode() verify_url='http://www.meiduo.site:8080/success_verify_email.html?token=' + token to_email=attrs['email'] subject="美多商城邮箱验证" html_message='<p>尊敬的用户您好!</p>' \ '<p>感谢您使用美多商城。</p>' \ '<p>您的邮箱为:%s 。请点击此链接激活您的邮箱:</p>' \ '<p><a href="%s">%s<a></p>' % (to_email, verify_url, verify_url) send_mial(subject,'',settings.EMAIL_FROM,[to_email],html_message=html_message) return instance #邮箱验证时前端发送请求,携带之前的token值, 后端取出token值,修改邮箱状态 class UserEmailVerifyView(APIView): def get(self,request): token=request.query_params.get('token') tjs=TJS(setting.SECRET_KEY,300) data=tjs.loads(token) user_id=data.get('user_id') email=data.get('email') user=User.objects.get(id=user_id,email=email) user.email_active=True user.save() return Response('OK')