2010-05-07 25 views
54

我希望你能幫助我找出使用密碼實現手動(服務器端啓動)登錄而不使用的最佳方式。讓我來解釋的工作流程:手動登錄沒有密碼的用戶

  • 用戶註冊
  • 謝謝!與激活鏈接的電子郵件已經發送blablabla
  • (賬戶現在存在,但被標記爲未啓用)
  • 用戶打開郵件,點擊鏈接
  • (帳戶啓用)
  • 謝謝!您現在可以使用該網站

我想要做的是在用戶點擊郵件鏈接後登錄,以便他可以立即開始使用該網站。

我不能使用他的密碼,因爲它在數據庫中加密,是編寫自定義身份驗證後端的唯一選項嗎?

回答

78

您不需要密碼即可登錄用戶。auth.login function只需要一個User對象,當您啓用該帳戶時,您大概已從數據庫獲取該對象。所以你可以直接通過login

當然,你需要是非常請注意,用戶無法欺騙到現有的已啓用帳戶的鏈接,然後該帳戶將自動以該用戶的身份登錄。

from django.contrib.auth import login 

def activate_account(request, hash): 
    account = get_account_from_hash(hash) 
    if not account.is_active: 
     account.activate() 
     account.save() 
     user = account.user 
     login(request, user) 

...等

編輯

嗯,沒有注意到,規定使用authenticate,因爲它增加了額外的屬性。查看代碼,它所做的只是一個backend屬性,與驗證後端的模塊路徑相同。所以你可以僞造它 - 在上面的登錄電話之前,執行此操作:

user.backend = 'django.contrib.auth.backends.ModelBackend' 
+1

感謝;文檔同意,但也有以下警告: 「首先調用authenticate() 當您手動登錄用戶時,必須在調用login()之前調用authenticate()。authenticate()在用戶身上設置一個屬性,說明哪個驗證後端成功驗證了該用戶(請參閱後端文檔以瞭解詳細信息),並且稍後在登錄過程中需要此信息。「 這可能是一個問題嗎? – Agos 2010-05-07 10:18:39

+0

查看我的更新 – 2010-05-07 10:28:48

+0

儘管從django.contrib.conf導入設置並分配settings.AUTHENTICATION_BACKENDS更好,以防使用自定義後端。 – Arsham 2014-09-24 14:29:25

25

Daniel的答案非常好。

另一種方法是創建這樣的自定義授權後端https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#writing-an-authentication-backend這樣的HashModelBackend:

class HashModelBackend(object): 
    def authenticate(self, hash=None): 
     user = get_user_from_hash(hash) 
     return user 

    def get_user(self, user_id): 
     try: 
      return User.objects.get(pk=user_id) 
     except User.DoesNotExist: 
      return None 

,然後在設置安裝此:

AUTHENTICATION_BACKENDS = (
    'myproject.backends.HashModelBackend', 
    'django.contrib.auth.backends.ModelBackend', 
) 

那麼你的看法是像這樣:

def activate_account(request, hash): 
    user = authenticate(hash=hash) 
    if user: 
     # check if user is_active, and any other checks 
     login(request, user) 
    else: 
     return user_not_found_bad_hash_message 
2

您可以使用ska包,它具有實現無密碼登錄到Django。 ska與認證令牌一起工作,其安全性基於SHARED_KEY,對於涉及的所有參與方(服務器)應該相同。

在客戶端(請求無密碼登錄的一方),您將生成一個URL並使用ska對其進行簽名。示例:

from ska import sign_url 
from ska.contrib.django.ska.settings import SECRET_KEY 

server_ska_login_url = 'https://server-url.com/ska/login/' 

signed_url = sign_url(
    auth_user = 'test_ska_user_0', 
    secret_key = SECRET_KEY, 
    url = server_ska_login_url 
    extra = { 
     'email': '[email protected]', 
     'first_name': 'John', 
     'last_name': 'Doe', 
    } 
    ) 

標記的默認生存期爲600秒。您可以通過證明lifetime參數來自定義該參數。

在服務器端(用戶登錄的站點),考慮到您已正確安裝ska,用戶 在訪問URL(如果它們存在(用戶名匹配)或以其他方式創建時已登錄) 。您可以在項目的Django設置中自定義3個回調。

USER_GET_CALLBACK(string):如果成功從數據庫(現有用戶)獲取用戶,則觸發。 USER_CREATE_CALLBACK(字符串):在創建用戶(用戶不存在)後立即觸發。 USER_INFO_CALLBACK(string):成功驗證後觸發。

有關更多信息,請參閱文檔(http://pythonhosted.org/ska/)。

2

迴應的回答。

寫你的後端道:

from django.contrib.auth import get_user_model 
from django.contrib.auth.backends import ModelBackend 

class HashModelBackend(ModelBackend): 

def authenticate(self, username=None, **kwargs): 
    UserModel = get_user_model() 
    if username is None: 
     username = kwargs.get(UserModel.USERNAME_FIELD) 
    try: 
     user = UserModel._default_manager.get_by_natural_key(username) 
     return user 
    except UserModel.DoesNotExist: 
     return None 

答案是基於django.contrib.auth.backends.ModelBackend源代碼。這是實際的Django的1.9

我寧願下面Django的默認位置的自定義後端:

AUTHENTICATION_BACKENDS = [ 
    'django.contrib.auth.backends.ModelBackend', 
    'yours.HashModelBackend', 
] 

因爲帳號激活不足可能比登錄本身。據https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#specifying-authentication-backends

AUTHENTICATION_BACKENDS事項的順序,因此,如果同樣的用戶名和密碼在多個後端有效,Django會在第一次正面比賽停止處理。

注意 這段代碼甚至不正確的密碼驗證用戶。

+0

這段代碼中有語法錯誤,所以不清楚應該發生什麼('hash'被定義但未被使用,'username'被使用但未被定義)。 – 2016-08-16 10:15:32

+0

@ amichai-schreiber謝謝!我已經更正了答案 – 2016-08-17 12:59:27

14

從Django 1.10開始,過程已經簡化。

在所有版本的Django中,爲了讓用戶登錄,它們必須是authenticated by one of your app's backends(由AUTHENTICATION_BACKENDS設置控制)。

如果你只是想強行登陸,你可以要求用戶通過從該列表中的第一個後端認證:

from django.conf import settings 
from django.contrib.auth import login 


# Django 1.10+ 
login(request, user, backend=settings.settings.AUTHENTICATION_BACKENDS[0]) 

# Django <1.10 - fake the authenticate() call 
user.backend = settings.AUTHENTICATION_BACKENDS[0] 
login(request, user)