token的使用基於Python的登入註冊功能
使用基於 Token 的身份驗證方法,在服務端不需要儲存使用者的登入記錄。大概的流程是這樣的:
- 客戶端使用使用者名稱跟密碼請求登入
- 服務端收到請求,去驗證使用者名稱與密碼
- 驗證成功後,服務端會簽發一個 Token,再把這個 Token 傳送給客戶端
- 客戶端收到 Token 以後可以把它儲存起來,比如放在 Cookie 裡或者 Local Storage 裡
- 客戶端每次向服務端請求資源的時候需要帶著服務端簽發的 Token
- 服務端收到請求,然後去驗證客戶端請求裡面帶著的 Token,如果驗證成功,就向客戶端返回請求的資料
1.jwt的資料返回設定
重寫jwt_response_payload_handler 可以實現 返回token和其他資料
登入成功後 檢視除錯工具中Resource 中 localstorage ,其中會存放token
''' utils/users.py '''
# user 就是我們認證之後的user
def jwt_response_payload_handler(token, user=None, request=None):
return {
'token':token,
'username':user.username,
'user_id':user.id
}
''' settings.py'''
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
) ,
}
# 設定jwt 返回資料的函式
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER':'utils.user.jwt_response_payload_handler'
}
2.註冊成功之後返回token
筆者出現過的bug:在create方法中沒有給user物件新增token欄位時,註冊賬號,之後在create方法中新增token欄位後,用該賬號登入時,其物件並沒有token欄位, 所以會請求不到token。
確認註冊完成後 生成token
即在資料入庫後 註冊完成 生成token
點選註冊完成後 觸發的一系列事件
其中在create中 save入庫後 即註冊完成
生成token 的程式碼:
def create():
...
# 此處注意導包 很容易匯入錯誤的包
from rest_framework_jwt.settings import api_settings
# 需要獲取2個方法
jwt_payload_handler = api.settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api.settings.JWT_ENCODE_HANDLER
# 1.jwt_payload_handler需要將使用者的資訊傳遞給這個函式,函式自己會獲取user中的資料
# 獲取之後,會生成一個字串
payload = jwt_payload_handler(user)
# 2.需要對payload 進行編碼 編碼之後的才是token
token = jwt_encode_handler(payload)
# 序列化的時候返回token
# 序列化器中有該欄位 但模型類中沒有這個欄位
# 所以動態新增屬性
user.token = token
return user
######針對上面 user.token = token 舉的例子 #########
def Person(object):
name = ''
p = Person()
p.name = 'it'
print(p.name)
# 這個可以列印 動態新增屬性 只對當前物件起作用
p.age = 20
print(p.age)
# 這個不能列印
p2 = Person()
print(p2.age)
''' serializer.py ''''
class RegisterCreateSerializer(serializers.ModelSerializer):
...
# 新增新欄位
token = serializers.CharField(label='token',read_only=True)
# token 只是應用於 序列化(物件轉換為JSON)操作的時候使用,所以只能讀 需要新增read_only
# 不加read_only會報錯 提示 token欄位為必填項
# read_only 表示只在序列化的時候該欄位起作用
class Meta:
model = Users
fields = ('id', 'username', 'password', 'password2', 'sms_code', 'mobile', 'allow', 'token') # 增加token
''' js/register.js '''
在前端檔案中增加儲存token
var vm = new Vue({
...
methods: {
on_submit: function(){
axios.post(...)
.then(response => {
// 記錄使用者的登入狀態
sessionStorage.clear();
localStorage.clear();
localStorage.token = response.data.token;
localStorage.username = response.data.username;
localStorage.user_id = response.data.id;
location.href = '/index.html';
})
.catch(...)
}
}
})
3.多賬號登入 認證
增加支援使用者名稱與手機號均可作為登入賬號
JWT擴充套件的登入檢視,在收到使用者名稱與密碼時,也是呼叫Django的認證系統中提供的authenticate()來檢查使用者名稱與密碼是否正確。
我們可以通過修改Django認證系統的認證後端(主要是authenticate方法)來支援登入賬號既可以是使用者名稱也可以是手機號。
修改Django認證系統的認證後端需要繼承django.contrib.auth.backends.ModelBackend,並重寫authenticate方法。
authenticate(self, request, username=None, password=None, **kwargs)方法的引數說明:
request 本次認證的請求獨享
username 本次認證提供的使用者賬號
password 本次認證提供的密碼
我們想要讓使用者既可以以使用者名稱登入,也可以以手機號登入,那麼對於authenticate方法而言,username引數即表示使用者名稱或者手機號。
重寫authenticate方法的思路:
根據username引數查詢使用者User物件,username引數可能是使用者名稱,也可能是手機號
若查詢到User物件,呼叫User物件的check_password方法檢查密碼是否正確
''' utils/users.py '''
def get_user_by_account(username):
# 根據使用者輸入的資訊 判斷使用者輸入的是手機號還是使用者名稱
try:
if re.match('1[3-9]\d{9}',username):
# 使用者名稱滿足手機號的規則
user = Users.objects.get(mobile=username)
else:
user = Users.objects.get(username=username)
except Users.DoesNotExist:
user = None
return user
class UsernameMobileAuthModelBackend(ModelBackend):
def authenticate(self,request,username=None,password=None,**kwargs):
# 1. 先判斷輸入型別 再進行查詢
user = get_user_by_account(username)
# 2. 校驗
if user is not None and user.check_password(password):
return user
return None
class SettingsBackend(object):
"""
Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.
Use the login name and a hash of the password. For example:
ADMIN_LOGIN = 'admin'
ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M='
"""
def authenticate(self, request, username=None, password=None):
# 1. 根據使用者在使用者名稱的地方輸入的內容進行判斷,如果使用者輸入的是手機號,我們就根據手機號查詢使用者
# 否則就根據 使用者名稱進行查詢
user = get_user_by_account(username)
# 2如果使用者查詢出來,我們再校驗密碼
if user is not None and user.check_password(password):
return user
return None
def get_user(self, user_id):
try:
return Users.objects.get(pk=user_id)
except Users.DoesNotExist:
return None
需要改變認證後端的類
在配置檔案中告知Django使用我們自定義的認證後端
'''settings.py '''
# 改變認證後端的類
AUTHENTICATION_BACKENDS = [
# 'utils.users.UsernameMobileAuthModelBackend',
'utils.users.SettingsBackend'
]
# 不加 這個 會報
# {
# "non_field_errors": [
# "無法使用提供的認證資訊登入。"
# ]
# }
Django REST framework JWT提供了登入獲取token的檢視,可以直接使用,在users應用中的urls新增路由資訊
''' urls.py '''
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
#新增登入認證
url(r'^auths/',obtain_jwt_token),
]