JWT與cookie和token的區別,django中使用
阿新 • • 發佈:2018-12-30
一. cookie
A) cookie如何認證
1. 使用者輸入使用者名稱與密碼,傳送給伺服器。
2. 伺服器驗證使用者名稱和密碼,正確的就建立一個會話(session),同時會把這個會話的ID儲存到客戶端瀏覽器中,因為儲存的地方是瀏覽器的cookie,所以這種認證方式叫做基於cookie的認證方式。
3. 後續的請求中,瀏覽器會發送會話ID到伺服器,伺服器上如果能找到對應的ID的會話,那麼伺服器就會返回需要的資料給瀏覽器。
4. 當用戶退出登入,會話會同時在客戶端和伺服器端被銷燬。
B) cookie認證方式的不足之處
1. 伺服器要為每個使用者保留session資訊,連線使用者過多會造成伺服器記憶體壓力過大。 2. 適合單一域名,不適合第三方請求。
二. token
A) token的認證過程
1. 使用者輸入使用者名稱和密碼,傳送給伺服器。
2. 伺服器驗證使用者名稱和密碼,正確的話就返回一個簽名過的token(token 可以認為就是個長長的字串),瀏覽器客戶端拿到這個token。
3. 後續每次請求中,瀏覽器會把token作為http header傳送給伺服器,伺服器驗證簽名是否有效,如果有效那麼認證就成功,可以返回客戶端需要的資料。
4. 一旦使用者退出登入,只需要客戶端銷燬token即可,伺服器端不需要任何操作。
B) token認證方式的特點
客戶端的token中自己保留有大量資訊,伺服器沒有儲存這些資訊,而只負責驗證,不必進行資料庫查詢,執行效率大大提高。
三. JWT
A) JWT介紹
1. JWT是json web token縮寫。它將使用者資訊加密到token裡,伺服器不儲存任何使用者資訊。伺服器通過使用儲存的金鑰驗證token的正確性,只要正確即通過驗證。
2. 優點是在分散式系統中,很好地解決了單點登入問題,很容易解決了session共享的問題。jwt長度較小,且可以使用URL傳輸(URL safe)。不想cookies只能在web環境起作用。 JWT可以同時使用在web環境和RESTfull的介面。
缺點是無法作廢已頒佈的令牌/不易應對資料過期。
B) JWT組成
JWT包含三個部分: Header頭部,Payload負載和Signature簽名。由三部分生成token,三部分之間用“.”號做分割。 列如 : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 1. Header 在Header中通常包含了兩部分:type:代表token的型別,這裡使用的是JWT型別。 alg:使用的Hash演算法,例如HMAC SHA256或RSA. { "alg": "HS256", "typ": "JWT" } 這會被經過base64Url編碼形成第一部分 2. Payload token的第二個部分是荷載資訊,它包含一些宣告Claim(實體的描述,通常是一個User資訊,還包括一些其他的元資料) 宣告分三類: 1)Reserved Claims,這是一套預定義的宣告,並不是必須的,這是一套易於使用、操作性強的宣告。包括:iss(issuer)、exp(expiration time)、sub(subject)、aud(audience)等 2)Plubic Claims, 3)Private Claims,交換資訊的雙方自定義的宣告 { "sub": "1234567890", "name": "John Doe", "admin": true } 同樣經過Base64Url編碼後形成第二部分 3. signature 使用header中指定的演算法將編碼後的header、編碼後的payload、一個secret進行加密。 例如使用的是HMAC SHA256演算法,大致流程類似於: HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 這個signature欄位被用來確認JWT資訊的傳送者是誰,並保證資訊沒有被修改
C) 為什麼要使用JWT
相比XML格式,JSON更加簡潔,編碼之後更小,這使得JWT比SAML更加簡潔,更加適合在HTML和HTTP環境中傳遞。 在安全性方面,SWT只能夠使用HMAC演算法和共享的對稱祕鑰進行簽名,而JWT和SAML token則可以使用X.509認證的公私祕鑰對進行簽名。與簡單的JSON相比,XML和XML數字簽名會引入複雜的安全漏洞。 因為JSON可以直接對映為物件,在大多數程式語言中都提供了JSON解析器,而XML則沒有這麼自然的文件-物件對映關係,這就使得使用JWT比SAML更方便 java json web token工具類
D) JWT的django引入
1、前端請求頭帶上token:
axios.get(this.host + '/user/', {
// 向後端傳遞JWT token的方法
headers: {
'Authorization': 'JWT ' + this.token
},
responseType: 'json',
})
-------------------------------------------------------------------------------------
2、django中安裝jwt
pip install djangorestframework-jwt
3、配置,指明有效期
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
}
-------------------------------------------------------------------------------------
4、使用
序列化器中:
token = serializers.CharField(label='登入狀態token', read_only=True) # 增加token欄位
fields = ('id', 'username', 'password', 'password2', 'sms_code', 'mobile', 'allow', 'token') # 增加token
create方法中:
# 補充生成記錄登入狀態的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
前端js檔案中加入儲存token方法
// 記錄使用者的登入狀態
sessionStorage.clear();
localStorage.clear();
localStorage.token = response.data.token;
注意:此次操作使用token儲存在sessionStorage中,關閉頁面也儲存,儲存在localStorage中。
---------------------------------------------------------------------------------------
5、使用JWT進行django登入
配置路由urlpatterns = [
url(r'authorizations/', obtain_jwt_token, name='authorizations'),
]
但是預設的返回值僅有token,我們還需在返回值中增加username和user_id。
在users/utils.py 中,建立
def jwt_response_payload_handler(token, user=None, request=None):
"""
自定義jwt認證成功返回資料
"""
return {
'token': token,
'user_id': user.id,
'username': user.username
}
修改配置檔案
# JWT
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
'JWT_RESPONSE_PAYLOAD_HANDLER': 'users.utils.jwt_response_payload_handler',
}
------------------------------------------------------------------------------
6、登入時使用使用者名稱或者手機號同時登入
在users/utils.py中編寫:
def get_user_by_account(account):
"""
根據帳號獲取user物件
:param account: 賬號,可以是使用者名稱,也可以是手機號
:return: User物件 或者 None
"""
try:
if re.match('^1[345789]\d{9}$', account):
# 帳號為手機號
user = User.objects.get(mobile=account)
else:
# 帳號為使用者名稱
user = User.objects.get(username=account)
except User.DoesNotExist:
return None
else:
return user
class UsernameMobileAuthBackend(ModelBackend):
"""
自定義使用者名稱或手機號認證
"""
def authenticate(self, request, username=None, password=None, **kwargs):
user = get_user_by_account(username)
if user is not None and user.check_password(password):
return user
配置檔案加入:
AUTHENTICATION_BACKENDS = [
'users.utils.UsernameMobileAuthBackend',
]