一、JWT原理:
參考文章:https://www.jianshu.com/p/180a870a308a
1、傳統的登錄方式:
瀏覽器輸入用戶名密碼,服務端校驗通過,根據用戶信息生成一個token,將token和user_id存到數據庫或者session中,並將token返回給前端,存入cookie,后面瀏覽器每次請求都會帶上cookie,服務端根據cookie查詢用戶,驗證用戶有效性。
弊端:
1)如果出現XSS(跨站腳本攻擊)漏洞,由於cookie可以被js讀取,XSS漏洞會導致用戶token被泄露。
解決方案:
a)設置httpOnly,這樣的話cookie將不會被js讀取,瀏覽器會自動將它加到請求頭信息中,但是帶來了新的問題,很容易被XSRF(跨站請求偽造)攻擊,因為只要當前瀏覽器開着,另一個界面可以很容易的跨站請求這個界面的內容,因為cookie會被默認發送出去。
b)設置secure,這樣cookie就只能通過https傳輸,可以過濾掉一些使用http協議請求的XSS注入。
2)將驗證信息存到數據庫中,每次驗證的時候,都需要去數據庫中查詢,增加了數據庫的查詢和存儲開銷。
3)如果將token存到session中,也會增加服務器的存儲壓力。
2、為什么使用 JWT
1)可以通過URL POST參數或者http header中發送,數據量小,傳輸速度快。
2)自包含:負載中包含了用戶所需要的所有信息,避免多次查詢數據庫。
3)JWT組成:
header+payload+signature 三者通過點【.】連接起來就是JWT了。
a)header頭部:包含token類型和采用的加密算法
{"alg":"HS256", "typ":"JWT"}
b)payload負載:存放信息的地方,可以把用戶ID等信息存放在payload中。
常用的信息有:iss(簽發者), exp(過期時間), sub(面向用戶), aud(接收方), iat(簽發時間)等。
c)signature簽名:使用編碼后(base64編碼)的header和payload再加上我們提供的一個公鑰,然后使用header中指定的簽名算法進行簽名。作用是保證JWT沒有被竄改過。
JWT只適合向web端傳遞一些非敏感信息,因為base64編碼是可逆的,很容易被破解。
JWT通常用來設計用戶認證和授權系統,還有我們通常說的單點登錄等。
二、JWT的使用過程
1)前端通過表單將用戶名和密碼發送到后端接口。
2)后端核對用戶名和密碼后,將用戶的id及其他非敏感信息作為JWT Payload,將其與頭部分別進行base64編碼后簽名,生成JWT
具體源代碼見rest_framework_jwt下面的serializer中,有興趣研究的可以去跟蹤看看
3)后端將JWT字符串作為登錄成功的結果返回給前端,前端可以將JWT存到localStorage或者sessionStorage中,退出登錄時,前端刪除保存的JWT信息即可。
4)前端每次在請求時,將JWT放到header中的Authorization
5)后端驗證JWT的有效性
6)驗證通過后,進行其他邏輯操作。
三、JWT使用方法:
1、安裝
pip install djangorestframework-jwt
2、將JSONWebTokenAuthentication加到settings.py中的DEFAULT_AUTHENTICATION_CLASSES中,它可以將用戶post過來的數據進行驗證,並將user取出來,它和MIDDLEWARE中的AuthenticationMiddleware的原理是一樣的。
3、配置JWT的認證接口的url
from djangorestframework-jwt.views import obtain_jwt_token
url(r"^login$", obtain_jwt_token)
4、前端訪問 login,傳入用戶名和密碼,服務器會給瀏覽器返回一段token(jwt)
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE1MzEzNzU4NjEsImVtYWlsIjoiMzcwMTkzNTkzQHFxLmNvbSIsInVzZXJuYW1lIjoiYWRtaW4ifQ.BIl1b68VV-LvZOu-wrUeMxUFI3IREapgqfbTtzB6Ftw"}
5、在后面的請求中,將JWT token信息 加入到header中的authorization中。
6、擴展:
DRF默認是通過郵箱作為用戶名來登錄的,所以如果我們是通過手機或者其他方式登錄的話,需要重寫DRF的認證方式。
from django.db.models import Q
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
User = get_user_model()
class CustomBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
user = User.objects.get(Q(username=username) | Q(mobil = username))
if user.check_password(password):
return user
else:
return None
except Exception as e:
return None
7、將重寫的CustomBackend加入到settings中
8、JWT的額外設置: