登入介面設計和實現
---恢復內容開始---
1、使用者功能設計與設計:
提供使用者註冊處理
提供使用者登入處理
提供路由配置
2、使用者登入介面設計:
接受使用者通過POST 方法提交的登入資訊,提交的資料是JSON 格式資料。
從user表中 email 找出匹配的一條記錄, 驗證密碼是否正確
驗證通過說明是合法的使用者登入,顯示歡迎頁面
驗證失敗返回錯誤狀態碼,4XX
整個過程都採用AJAX非同步過程,使用者提交JSON 資料,服務端回去資料後處理,返回JSON
URL: /user/login
METHOD:POST
3、路由配置:
4、登入程式碼:
1 # 登入業務 2 def login(request:HttpRequest): 3 play = simplejson.loads(request.body) 4 try: 5 # 一般推薦先有索引的放前面 6 email = play['email'] 7 user = User.objects.filter(email=email).get() 8 9 # 輸入密碼 和 資料庫中密碼對比 10 if bcrypt.checkpw(play['password'].encode(), user.password.encode()): 11 # 驗證通過 12 token = gen_token(user.id) 13 res = JsonResponse({ 14 'user':{ 15 "user_id":user.id, 16 "name":user.name, 17 "email":user.email 18}, 19 'token':token 20 }) 21 res.set_cookie('Jwt', token) # set cookie 22 return res 23 else: 24 return HttpResponseBadRequest(status=400) 25 except Exception as e: 26 return JsonResponse("輸入有誤")
1、將使用者資訊返回去,瀏覽器可以使用,處了密碼
2、伺服器端通過set_cookie 讓客戶端強行修改cookie(客戶端一般都開啟的cookie)
5、介面認證:
如何獲取瀏覽器提交的token資訊?
1、使用Header中的Authorization
通過這個header增加token資訊
通過header傳送資料,方法可以是post , get
2、自定義header
JWT來發送token
我們選擇第二種
認證:
基本上所有的業務都需要認證使用者的資訊
在這裡比較時間戳,如果過期,就直接拋未認證401,客戶端收到後就該直接跳轉到登入頁
如果沒有提交user id,就直接重新登入,如果使用者查到了,填充user物件。
request -》 時間戳比較-》 user id 比較 --》向後執行。
測試: 沒有報錯,就證明是沒有修改過的
提供一個函式,呼叫業務函式之前,看下,之前是否登陸過
1 # 只要需要認證的地方都可以加這個功能,是否登陸過 2 def auth(view_func): 3 def wrapper(request:HttpRequest): 4 token = request.META.get('HTTP_JWT', None) 5 print(token,type(token),'== = = = = ') # str 6 try:# 獲取到 token中的payload,證明是之前登入過的使用者 7 play = jwt.decode(token, KEY, algorithms=['HS256']) 8 print(play, type(play),'=============') 9 # 但是雖然是之前的,但是得確保,這一時刻,資料庫中還存在這個使用者嗎,是否啟用狀態等 10 user = User.objects.get(pk=play["user_id"]) 11 print(user,'==== = = = = = = == ') 12 # 如果查到了,接下來的業務處理: 13 if user: 14 #查到,也就是已經登入,要利用這些使用者資訊,所以要動態註冊到request中 15 request.user = user # 這樣,下面執行show方法的時候,就可以利用這個使用者屬性了 16 # 如果沒查到,user就報錯了,所以這塊沒必要寫 17 # else: 18 # return HttpResponseBadRequest() 19 except Exception as e: 20 print(e) 21 return HttpResponseBadRequest('n or p 錯誤') 22 ret = view_func(request) 23 # 這個中間還可以執行去其他的業務。。。。。 24 return ret 25 return wrapper 26 27 @auth -----裝飾器實現 28 def show(request:HttpRequest): 29 # 例如: 30 print(request.user,'==') 31 print(type(request.user)) 32 return JsonResponse({'1':7})
---恢復內容結束---
1、使用者功能設計與設計:
提供使用者註冊處理
提供使用者登入處理
提供路由配置
2、使用者登入介面設計:
接受使用者通過POST 方法提交的登入資訊,提交的資料是JSON 格式資料。
從user表中 email 找出匹配的一條記錄, 驗證密碼是否正確
驗證通過說明是合法的使用者登入,顯示歡迎頁面
驗證失敗返回錯誤狀態碼,4XX
整個過程都採用AJAX非同步過程,使用者提交JSON 資料,服務端回去資料後處理,返回JSON
URL: /user/login
METHOD:POST
3、路由配置:
4、登入程式碼:
1 # 登入業務 2 def login(request:HttpRequest): 3 play = simplejson.loads(request.body) 4 try: 5 # 一般推薦先有索引的放前面 6 email = play['email'] 7 user = User.objects.filter(email=email).get() 8 9 # 輸入密碼 和 資料庫中密碼對比 10 if bcrypt.checkpw(play['password'].encode(), user.password.encode()): 11 # 驗證通過 12 token = gen_token(user.id) 13 res = JsonResponse({ 14 'user':{ 15 "user_id":user.id, 16 "name":user.name, 17 "email":user.email 18 }, 19 'token':token 20 }) 21 res.set_cookie('Jwt', token) # set cookie 22 return res 23 else: 24 return HttpResponseBadRequest(status=400) 25 except Exception as e: 26 return JsonResponse("輸入有誤")
1、將使用者資訊返回去,瀏覽器可以使用,處了密碼
2、伺服器端通過set_cookie 讓客戶端強行修改cookie(客戶端一般都開啟的cookie)
5、介面認證:
如何獲取瀏覽器提交的token資訊?
1、使用Header中的Authorization
通過這個header增加token資訊
通過header傳送資料,方法可以是post , get
2、自定義header
JWT來發送token
我們選擇第二種
認證:
基本上所有的業務都需要認證使用者的資訊
在這裡比較時間戳,如果過期,就直接拋未認證401,客戶端收到後就該直接跳轉到登入頁
如果沒有提交user id,就直接重新登入,如果使用者查到了,填充user物件。
request -》 時間戳比較-》 user id 比較 --》向後執行。
測試: 沒有報錯,就證明是沒有修改過的
提供一個函式,呼叫業務函式之前,看下,之前是否登陸過
1 # 只要需要認證的地方都可以加這個功能,是否登陸過 2 def auth(view_func): 3 def wrapper(request:HttpRequest): 4 token = request.META.get('HTTP_JWT', None) 5 print(token,type(token),'== = = = = ') # str 6 try:# 獲取到 token中的payload,證明是之前登入過的使用者 7 play = jwt.decode(token, KEY, algorithms=['HS256']) 8 print(play, type(play),'=============') 9 # 但是雖然是之前的,但是得確保,這一時刻,資料庫中還存在這個使用者嗎,是否啟用狀態等 10 user = User.objects.get(pk=play["user_id"]) 11 print(user,'==== = = = = = = == ') 12 # 如果查到了,接下來的業務處理: 13 if user: 14 #查到,也就是已經登入,要利用這些使用者資訊,所以要動態註冊到request中 15 request.user = user # 這樣,下面執行show方法的時候,就可以利用這個使用者屬性了 16 # 如果沒查到,user就報錯了,所以這塊沒必要寫 17 # else: 18 # return HttpResponseBadRequest() 19 except Exception as e: 20 print(e) 21 return HttpResponseBadRequest('n or p 錯誤') 22 ret = view_func(request) 23 # 這個中間還可以執行去其他的業務。。。。。 24 return ret 25 return wrapper 26 27 @auth -----裝飾器實現 28 def show(request:HttpRequest): 29 # 例如: 30 print(request.user,'==') 31 print(type(request.user)) 32 return JsonResponse({'1':7})
Django的認證:
本專案使用了無session的機制,且使用者資訊自己建表管理,所以認證需要自己實現
6、中介軟體技術Midleware
官方定義:在Django的request 和response處理過程中,由框架提供的hook鉤子
中介軟體技術,在1.10之後,發生改變,使用新的定義方式
參看:https://docs.djangoproject.com/en/1.11/topics/http/middleware/#writing-your-own-middleware
中介軟體實現方式:有兩種
自定義的中介軟體,也要在這裡註冊:一般寫在最後,先用框架自己的中介軟體。
自己寫中介軟體:https://docs.djangoproject.com/en/1.11/topics/http/middleware/#writing-your-own-middleware
右擊中介軟體名,選擇copyreference,新增到settings.py中。
但是,這樣所有的請求和響應都攔截,我們還得判斷是不是訪問的想要攔截的view函式,所以,考慮其他的方法。中介軟體有很多用途,適當的攔截所有請求和響應,例如瀏覽器端的IP 是否被禁用,UserAgent分析,異常響應的統一處理。
例如本專案的認證,登入的時候,就不能用中介軟體,所以不適合註冊一箇中間件來攔截,而是使用裝飾器,需要攔截的就加上此功能。
裝飾器:
在需要認證 的view 函式上增加認證功能,寫一個裝飾器函式,誰需要認證,就在這個view函式上應用這個裝飾器
1 AUTH_EXPIRE = 8*60*60 # 8 小時過期,可以解除安裝settings中,通過from django.conf import settings呼叫 2 # 只要需要認證的地方都可以加這個功能,是否登陸過 3 def auth(view_func): 4 def wrapper(request:HttpRequest): 5 token = request.META.get('HTTP_JWT', None) 6 print(token,type(token),'== = = = = ') # str 7 try:# 獲取到 token中的payload,證明是之前登入過的使用者 8 play = jwt.decode(token, KEY, algorithms=['HS256']) 9 # 但是雖然是之前的,但是得確保,這一時刻,資料庫中還存在這個使用者嗎,是否啟用狀態等 10 # 驗證過期時間: 11 current = datetime.datetime.now().timestamp() 12 if (current - play.get('timestamp', 0)) > AUTH_EXPIRE: 13 return HttpResponse("過期了") 14 15 user = User.objects.get(pk=play["user_id"]) 16 # 如果查到了,接下來的業務處理: 17 if user: 18 #查到,也就是已經登入,要利用這些使用者資訊,所以要動態註冊到request中 19 request.user = user # 這樣,下面執行show方法的時候,就可以利用這個使用者屬性了 20 # 如果沒查到,user就報錯了,所以這塊沒必要寫 21 # else: 22 # return HttpResponseBadRequest() 23 except Exception as e: 24 print(e) 25 return HttpResponseBadRequest('n or p 錯誤') 26 ret = view_func(request) 27 # 這個中間還可以執行去其他的業務。。。。。 28 return ret 29 return wrapper 30 31 @auth # 很自由的應用在需要認證的view 函式上。 32 def show(request:HttpRequest): 33 # 例如: 34 print(request.user,'==') 35 print(type(request.user)) 36 return JsonResponse({'1':7})
Jwt 過期問題:(過期兩種方式:過期的起點時間 和 到期時間)
pyjwt 支援過期設定,在decode的時候,如果過期,則丟擲異常,
需要在payload中增加 claim exp。exp要求是一個整數int的時間戳。
1 # 只要需要認證的地方都可以加這個功能,是否登陸過 2 def auth(view_func): 3 def wrapper(request:HttpRequest): 4 token = request.META.get('HTTP_JWT', None) 5 print(token,type(token),'== = = = = ') # str 6 try:# 獲取到 token中的payload,證明是之前登入過的使用者 7 # 認證的同時,驗證過期時間:過期或拋異常(可以通過時間判斷,塊過期的時候,續期,set cookie重新發一次) 8 play = jwt.decode(token, KEY, algorithms=['HS256']) 9 # 但是雖然是之前的,但是得確保,這一時刻,資料庫中還存在這個使用者嗎,是否啟用狀態等 10 11 user = User.objects.get(pk=play["user_id"]) 12 # 如果查到了,接下來的業務處理: 13 if user: 14 #查到,也就是已經登入,要利用這些使用者資訊,所以要動態註冊到request中 15 request.user = user # 這樣,下面執行show方法的時候,就可以利用這個使用者屬性了 16 # 如果沒查到,user就報錯了,所以這塊沒必要寫 17 # else: 18 # return HttpResponseBadRequest() 19 except Exception as e: 20 print(e) 21 return HttpResponseBadRequest('n or p 錯誤') 22 ret = view_func(request) 23 # 這個中間還可以執行去其他的業務。。。。。 24 return ret 25 return wrapper
測試用的:exp claim,此程式碼,可以用於續期,通過判斷,快過期,就通過set cookie重新發一個jwt 過去。
1 import jwt 2 import datetime 3 import threading 4 5 event = threading.Event() 6 key = 'jerry' 7 # 在jwt 的payload中增加 exp claim 8 data = jwt.encode({'name':'jerry','exp':int(datetime.datetime.now().timestamp() + 3)}, key) 9 print(jwt.get_unverified_header(data))# 獲取沒有過期的頭 10 try: 11 while not event.wait(1): 12 print(jwt.decode(data, key)) 13 print(datetime.datetime.now().timestamp()) 14 except jwt.ExpiredSignatureError as e:# 過期就拋異常 15 print(e) 16 17 print(jwt.get_unverified_header(data))