153 多方式登入簽發token&單項/多項的增刪查改
上節回顧
1 jwt:重點(跟語言,框架無關)
-json web token
-cookie:客戶端瀏覽器上的鍵值對,資料不安全
-session:服務端的鍵值對(記憶體,資料庫,redis,檔案),安全,對服務端壓力大
-token:三段:頭.荷載.簽名
-header(公司資訊,加密方式。。。)
-payload(荷載,真正有用的資料部分,使用者id,使用者名稱字。。)
-signature(簽名,把頭和荷載部分通過加密演算法加密---》得到一個簽名)
2 drf-jwt模組
-快速使用:(預設使用的是auth的user表)
-1 建立使用者
-2 在路由中配置path('login/', obtain_jwt_token),
-3 在postman中測試,使用者名稱密碼輸入剛剛建立的使用者就可以生成token
-4 讓一個檢視必須登入以後才能訪問
-authentication_classes = [JSONWebTokenAuthentication, ]
-permission_classes = [IsAuthenticated,]
-5 讓一個檢視可以登入後訪問,也可以不登入訪問
-authentication_classes = [JSONWebTokenAuthentication, ]
-6 用postman測試,在請求頭中加入
-Authorization jwt adfasdf
-自己寫基於jwt的認證類(登入了能訪問,不登入就不能訪問)
class JwtAuthentication(BaseJSONWebTokenAuthentication):
def authenticate(self, request):
#token=request.GET.get('token')
token=request.META.get('HTTP_Authorization'.upper())
try:
# 驗證token是否正確
payload = jwt_decode_handler(token)
except jwt.ExpiredSignature:
raise AuthenticationFailed('過期了')
except jwt.DecodeError:
raise AuthenticationFailed('解碼錯誤')
except jwt.InvalidTokenError:
raise AuthenticationFailed('不合法的token')
user=self.authenticate_credentials(payload)
return (user, token)
-自定製認證類的使用方式:
-全域性使用
-區域性使用
3 base64編碼(跟語言無關,跟框架無關)
-不同語言的base64可以相互編碼解碼
-base64內建模組
-圖片的二進位制,有的時候也會以base64的形式編碼
4 drf的檢視(兩個,5個,9個,檢視集)+序列化器+自動生成路由
今日內容
1 基於jwt的多方式登陸
1 手機號+密碼 使用者名稱+密碼 郵箱+密碼
2 流程分析(post請求):
-路由:自動生成
-檢視類:ViewSet(ViewSetMixin, views.APIView)
-序列化類:重寫validate方法,在這裡面對使用者名稱和密碼進行校驗
3 程式碼實現
路由
path('login/', views.LoginViewSet.as_view({'post':'create'})),
檢視
class LoginViewSet(ViewSet):
def create(self, request, *args, **kwargs):
# 例項化得到一個序列化類的物件
# ser=LoginSerializer(data=request.data,context={'request':request})
ser = LoginSerializer(data=request.data)
# 序列化類的物件的校驗方法
ser.is_valid(raise_exception=True) # 欄位自己的校驗,區域性鉤子校驗,全域性鉤子校驗
# 如果通過,表示登入成功,返回手動簽發的token
token = ser.context.get('token')
username = ser.context.get('username')
return APIResponse(token=token, username=username)
# 如果失敗,不用管了
序列化類
from rest_framework import serializers
from app01.models import UserInfo
import re
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.utils import jwt_encode_handler, jwt_payload_handler
from rest_framework_jwt.views import obtain_jwt_token
class LoginSerializer(serializers.ModelSerializer):
username = serializers.CharField()
class Meta:
model = UserInfo
fields = ['username', 'password']
def validate(self, attrs):
# username可能是郵箱,手機號,使用者名稱
username = attrs.get('username')
password = attrs.get('password')
# 如果是手機號
if re.match('^1[3-9]\d{9}$', username):
# 以手機號登入
user = UserInfo.objects.filter(phone=username).first()
elif re.match('^.+@.+$', username):
# 以郵箱登入
user = UserInfo.objects.filter(email=username).first()
else:
# 以使用者名稱登入
user = UserInfo.objects.filter(username=username).first()
# 如果user有值並且密碼正確
if user and user.check_password(password):
# 登入成功,生成token
# drf-jwt中有通過user物件生成token的方法
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
# token是要在檢視類中使用,現在我們在序列化類中
# self.context.get('request')
# 檢視類和序列化類之間通過context這個字典來傳遞資料
self.context['token'] = token
self.context['username'] = user.username
return attrs
else:
raise ValidationError('使用者名稱或密碼錯誤')
2 自定義user表,簽發token,認證類
表模型
class MyUser(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
phone = models.CharField(max_length=32)
email = models.EmailField()
路由
path('login2/', views.MyLoginView.as_view()),
檢視
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
from rest_framework_jwt.views import obtain_jwt_token
class MyLoginView(APIView):
def post(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password')
# 如果是手機號
if re.match('^1[3-9]\d{9}$', username):
# 以手機號登入
user = MyUser.objects.filter(phone=username).first()
elif re.match('^.+@.+$', username):
# 以郵箱登入
user = MyUser.objects.filter(email=username).first()
else:
# 以使用者名稱登入
user = MyUser.objects.filter(username=username).first()
# 如果user有值並且密碼正確
if user and user.password == password:
# 登入成功,生成token
# drf-jwt中有通過user物件生成token的方法
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return APIResponse(token=token, username=user.username)
else:
return APIResponse(code=101, msg='使用者名稱或密碼錯誤')
3 book,publish,author表關係及抽象表建立
# 注意:以後所有的資料刪除,儘量用軟刪除,使用一個欄位標誌是否刪除,而不是真正的從資料庫中刪除
-好處:1 這樣刪除資料不會影響索引,不會導致索引失效
2 之前存的使用者資料還在,以備以後使用
# 表模型如下
# 抽象出一個基表(不再資料庫生成,abstract=True),只用來繼承
class BaseModel(models.Model):
is_delete = models.BooleanField(default=False)
create_time = models.DateTimeField(auto_now_add=True)
class Meta:
# 基表必須設定abstract,基表就是給普通Model類繼承使用的,設定了abstract就不會完成資料庫遷移完成建表
abstract = True
class Book(BaseModel):
name = models.CharField(max_length=16)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish = models.ForeignKey(to='Publish', db_constraint=False, on_delete=models.DO_NOTHING)
# 重點:多對多外來鍵實際在關係表中,ORM預設關係表中兩個外來鍵都是級聯
# ManyToManyField欄位不提供設定on_delete,如果想設定關係表級聯,只能手動定義關係表
authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
@property
def publish_name(self):
return self.publish.name
@property
def author_list(self):
# ll=[]
# for author in self.authors.all():
# ll.append({'name':author.name,'sex':author.get_sex_display()})
return [{'name': author.name, 'sex': author.get_sex_display()} for author in self.authors.all()]
class Publish(BaseModel):
name = models.CharField(max_length=16)
address = models.CharField(max_length=64)
class Author(BaseModel):
name = models.CharField(max_length=16)
sex = models.IntegerField(choices=[(0, '男'), (1, '女')], default=0)
class AuthorDetail(BaseModel):
mobile = models.CharField(max_length=11)
# 有作者可以沒有詳情,刪除作者,詳情一定會被級聯刪除
# 外來鍵欄位為正向查詢欄位,related_name是反向查詢欄位
author = models.OneToOneField(to='Author', related_name='detail', db_constraint=False, on_delete=models.CASCADE)
4 book表單增群增
class BookView(APIView):
def post(self, request, *args, **kwargs):
if isinstance(request.data, dict):
# 增一條
ser = serializer.BookSerializer(data=request.data)
ser.is_valid(raise_exception=True)
ser.save()
return APIResponse(data=ser.data)
elif isinstance(request.data, list):
# 增多條
ser = serializer.BookSerializer(data=request.data, many=True)
# 內部如何實現的?
# many=True,ser不是BookSerializer物件,而是ListSerializer物件,套了一個個的BookSerializer
print(type(ser))
ser.is_valid(raise_exception=True)
#
from rest_framework.serializers import ListSerializer
ser.save() # ListSerializer的save
return APIResponse(msg='增加%s條成功' % len(request.data))
5 book表單查群查
class BookView(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk', None)
if pk:
# 單查
# 方式一
# book=models.Book.objects.filter(id=pk).filter(is_delete=False).first()
# if not book:
# raise Exception('要查詢的不存在')
# 方式二
book = models.Book.objects.get(id=pk, is_delete=False)
ser = serializer.BookSerializer(instance=book)
else:
# 查所有
book_list = models.Book.objects.all().filter(is_delete=False)
ser = serializer.BookSerializer(instance=book_list, many=True)
return APIResponse(data=ser.data)
6 book表單改群改
class BookView(APIView):
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk', None)
if pk:
# 單條修改
book = models.Book.objects.get(id=pk, is_delete=False)
ser = serializer.BookSerializer(instance=book, data=request.data)
ser.is_valid(raise_exception=True)
ser.save()
return APIResponse(msg='修改成功')
else:
# 分析:ListSerializer的update方法沒有寫,需要我們自己寫
from rest_framework.serializers import ListSerializer
# pks=[item['id'] for item in request.data]
# 如果不重寫ListSerializer的update方法,這是存不進去的
pks = []
for item in request.data:
pks.append(item['id'])
item.pop('id')
print(request.data)
book_list = models.Book.objects.filter(id__in=pks, is_delete=False)
ser = serializer.BookSerializer(instance=book_list, data=request.data, many=True)
print(type(ser))
ser.is_valid(raise_exception=True)
ser.save()
return APIResponse(msg='修改%s條成功')
# 你們能想到的方法
# pks = []
# for item in request.data:
# pks.append(item['id'])
# item.pop('id')
# book_list = models.Book.objects.filter(id__in=pks, is_delete=False)
#
# for i,book in enumerate(book_list):
# ser = serializer.BookSerializer(instance=book, data=request.data[i])
# ser.is_valid(raise_exception=True)
# ser.save()
# return APIResponse(msg='修改%s條成功'%len(book_list))
7 book表的單刪群刪
class BookView(APIView):
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk', None)
pks = []
if pk:
# 單條刪除
# res=models.Book.objects.filter(id=pk).update(is_delete=True)
# print(res)
# return APIResponse(msg='刪除成功')
pks.append(pk)
else:
pks = request.data
res = models.Book.objects.filter(id__in=pks).update(is_delete=True)
if res >= 1:
return APIResponse(msg='刪除%s條成功' % res)
else:
# raise Exception('沒有要刪除的資料')
return APIResponse(code=999, msg='沒有要刪除的資料')
8 序列化類
from app01 import models
class ListBookSerializer(serializers.ListSerializer):
# def create(self, validated_data):
# print('=======',validated_data)
# return '1'
def update(self, instance, validated_data):
print(instance) # book_list:是一堆圖書物件
print(validated_data) # 列表套字典,是要修改的資料
return [self.child.update(book, validated_data[i]) for i, book in enumerate(instance)]
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
list_serializer_class=ListBookSerializer # 指定many=True的時候,生成的ListBookSerializer的物件了
fields = ['name', 'price', 'publish', 'authors', 'publish_name', 'author_list']
extra_kwargs = {
'publish': {'write_only': True},
'authors': {'write_only': True},
'publish_name': {'read_only': True},
'author_list': {'read_only': True},
}
# def create(self, validated_data):
# print(validated_data)
9 路由
path('books/', views.BookView.as_view()),
re_path('books/(?P<pk>\d+)', views.BookView.as_view()),
擴充
1 自定義token的簽發和認證:https://www.cnblogs.com/liuqingzheng/articles/9766397.html
作業
1 基於jwt實現多方式登入(自己定義的使用者表),封裝全域性異常,封裝Response類
2 book的8個介面寫完
3 (擴充)publish,author8個介面寫完
相關文章
- Numpy array資料的增、刪、改、查例項
- [增刪改查] 最簡單的 JPA 一對多/多對一 CRUD 設計
- python 連線mongodb實現增刪改查例項PythonMongoDB
- Node+Express+MySql實現簡單增刪改查和登入ExpressMySql
- 單表增刪改查
- 2020-11-03登入註冊增刪改查
- [增刪改查] 最規範的 JPA 一對多/多對一 CRUD 示例
- CoreData - 簡單 增刪改查
- Vue+Mock.js模擬登入和表格的增刪改查VueMockJS
- 增刪改查
- 使用Django開發簡單介面:文章增刪改查Django
- 列表的增刪改查
- 字典的增刪改查
- layui的增刪改查UI
- mybatis的增刪改查MyBatis
- redist的增刪改查Redis
- layui+ssm簡單增刪改查UISSM
- Golang中map的三種宣告方式和簡單實現增刪改查Golang
- Java實現簡單的增刪改查操作Java
- SQL增刪改查SQL
- Mongoose查增改刪Go
- indexedDB 增刪改查Index
- mysql增刪改查MySql
- 使用mybatis開發的增刪改查操作MyBatis
- JavaScript中陣列的增刪改查以及應用方式JavaScript陣列
- 單連結串列實現增刪改查
- 多項式
- win10怎麼刪除右鍵多餘選項 win10刪除右鍵多餘選項的方法Win10
- mongodb 基本增刪改查MongoDB
- MongoDB增刪改查操作MongoDB
- EFCore之增刪改查
- sql指令,增,刪,查,改SQL
- mysql基本增刪改查MySql
- spring data mongodb 如何以事物的方式進行增刪改查SpringMongoDB
- win10刪除多餘啟動項怎麼操作 win10如何刪除多餘的開機啟動項Win10
- Mybatis-plus實現簡單增刪改查MyBatis
- 02-CoreData 的增刪改查
- cobra.Command的增刪改查