2017-10-14 80 views
0

我目前正在將CGI應用程序遷移到Dancer2。我以前使用了使用MySQL的「手工製作」身份驗證機制,並使用了屬性爲email,passwordstate的用戶表。 state表示賬戶是active還是lockedlocked表示帳戶被禁用(邏輯刪除)。如何使用Dancer2 :: Plugin :: Auth :: Extensible處理鎖定/禁用的用戶帳戶?

我也有表rolesuser_roles來實現我的兩個角色:管理員和用戶。

一切工作就像一個魅力,但有一個例外:

用我的舊的「手工製作」的機制,我能夠用戶,即邏輯刪除而不從數據庫中刪除它們。只有當電子郵件和hash_of(密碼)匹配帳戶未被鎖定時,登錄纔會成功。

如何使用Dancer2::Plugin::Auth::ExtensibleDancer2::Plugin::Auth::Extensible::Provider::Database來實現?

我希望鉤after_authenticate_user可以返回truefalse覆蓋的authenticate_user的結果,但事實並非如此。至少,它沒有記錄。

我想到的一件事是有一個額外的角色active,然後 - 對於每個路線 - require_role active而不是隻有require_login

所以我的問題是:我如何才能使Dancer2::Plugin::Auth::Extensible只考慮active用戶?

+0

我不明白爲什麼有人會低估你的問題。它寫得很好,顯示了一些研究(我們認爲閱讀源不應該被期望,或根本沒有問題),它清楚地說明你想要做什麼以及爲什麼,並且有一個代碼示例是這裏沒有關係。 – simbabque

+0

我創建了一個票據將其移入文檔:https://github.com/PerlDancer/Dancer2-Plugin-Auth-Extensible-Provider-Database/issues/5 – simbabque

回答

2

Borodin suggested創建視圖並將其用作用戶表。我已經做了一些測試,可以說這確實是實現這一點的最簡單方法。

警告:由於視圖的性質,這使得應用程序無法修改或添加用戶!

請考慮以下Dancer2申請。我從dancer2創建腳本開始。

$ dancer2 gen -a Foo 
$ cd Foo 

我創建了以下簡單的sqlite數據庫。

$ echo " 
CREATE TABLE users (
    id  INTEGER  PRIMARY KEY AUTOINCREMENT, 
    username VARCHAR(32) NOT NULL UNIQUE, 
    password VARCHAR(40) NOT NULL, 
    disabled TIMESTAMP NULL 
); 

CREATE VIEW active_users (id, username, password) AS 
    SELECT id, username, password FROM users WHERE disabled IS NULL; 

INSERT INTO users (username, password, disabled) 
VALUES ('foo', 'test', null), 
     ('bar', 'test', '2017-10-01 10:10:10'); 
" | sqlite3 foo.sqlite 

只有一個users表由插件建議的默認列,加上列disabled,其可以是或NULL一個時間戳。我認爲用禁用來說明比用有效更容易說明。我想對lib/Foo.pm做以下修改。所有這些基本上來自Dancer2::Plugin::Auth::ExtensibleDancer2::Plugin::Auth::Extensible::Provider::Database的文檔。

package Foo; 
use Dancer2; 
use Dancer2::Plugin::Database; 
use Dancer2::Plugin::Auth::Extensible; 

our $VERSION = '0.1'; 

get '/' => sub { 
    template 'index' => { 'title' => 'Foo' }; 
}; 

get '/users' => require_login sub { 
    my $user = logged_in_user; 
    return "Hi there, $user->{username}"; 
}; 

true; 

接下來,插件需要進入配置。編輯config.yml並用此替換它。

appname: "Foo" 
layout: "main" 
charset: "UTF-8" 
template: "simple" 
engines: 
    session: 
    Simple: 
     cookie_name: testapp.session 

# this part is interesting 
plugins: 
    Auth::Extensible: 
     realms: 
      users: 
       provider: 'Database' 

############### here we set the view 
       users_table: 'active_users' 
    Database: 
     driver: 'SQLite' 
     database: 'foo.sqlite' 
     on_connect_do: ['PRAGMA foreign_keys = ON'] 
     dbi_params: 
      PrintError: 0 
      RaiseError: 1 

現在我們都準備嘗試。

$ plackup bin/app.psgi 
HTTP::Server::PSGI: Accepting connections at http://0:5000/ 

在您的瀏覽器上訪問http://localhost:5000/users。你會看到默認的登錄表單。

login page

輸入footest。這應該工作,你應該看到/users路線。 (或者不是,就像我的情況那樣,重定向似乎被打破了......)。

foo is logged in

現在去http://localhost:5000/logout擺脫Foo的餅乾和開放http://localhost:5000/users一次。這次輸入bartest

你會看到登錄不起作用。

bar cannot log in

要反測試,更換config.ymlusers_table並重新啓動應用程序。

# config.yml 
       users_table: 'users' 

現在用戶foo將能夠登錄。因爲數據庫處理所有的邏輯(並且很可能已經緩存了它),所以這種方法不僅易於實現,而且也應該是具有最高性能的方式。

您的應用程序,特別是身份驗證插件,也不需要了解活躍禁用領域都存在。他們不需要關心。東西只會工作。

+0

您可能希望做一些奇特的事情來允許活動的用戶名與重複的禁用用戶名重複。也許是對多列的限制? 'CONSTRAINT unique_name UNIQUE(username,disabled)'可以工作,但不是很漂亮。 – Borodin

+0

@Borodin我不確定這是否是一個好主意。那麼用戶不再是唯一的。對於審計而言,這可能是相關的。 – simbabque

+0

哇!謝謝你的兩個很好的答案。你甚至提交了D2的FAQ。我認爲作爲第一個鏡頭,我將在視圖中使用該解決方案,因爲這更容易實施。從長遠來看,我可能會深入挖掘並編寫自己的Provider,它完全集成了'state'列。我的應用程序也有一個可以鎖定和解鎖用戶的管理頁面,所以如果['update_user'](https://metacpan.org/pod/Dancer2::Plugin::Auth::Extensible# update_user)函數可以無縫工作(包括'state'列)。 – PerlDuck

1

您可以繼承Dancer2::Plugin::Auth::Extensible::Provider::Database幷包裝get_user_details方法來檢查用戶是否處於活動狀態。

考慮我使用的相同的應用程序in my other answer。添加以下課程。

package Provider::Database::ActiveOnly; 

use Moo; 
extends 'Dancer2::Plugin::Auth::Extensible::Provider::Database'; 

around 'get_user_details' => sub { 
    my $orig = shift; 
    my $self = shift; 

    # do nothing if we there was no user 
    my $user = $self->$orig(@_) or return; 

    # do nothing if the user is disabled 
    return if $user->{disabled}; 

    return $user; 
}; 

1; 

代碼很簡單。用戶查看後,我們有用戶數據,所以我們可以檢查disabled列。如果有任何內容,用戶被禁用,我們中止。

您還需要對config.yml進行以下更改。

# config.yml 
plugins: 
    Auth::Extensible: 
     realms: 
      users: 
       provider: 'Provider::Database::ActiveOnly' 
       users_table: 'users' 

現在應用程序的行爲應該與其他答案完全相同。


要理解爲什麼這個工作,我們需要看看來源。認證發生在authenticate_user。起初我認爲這應該被替換,但是這個解決方案更聰明,因爲我們只需要獲取一次用戶數據。

authenticate_user方法fetches the user datathe get_user_details method,所以我們可以在那裏掛鉤。我們的around wrapper將透明地注入用戶活躍性的檢查,而其餘的代碼甚至不知道有什麼區別。

非活動用戶不會出現在與Plugin :: Auth :: Extensible相關的任何交互中。

+1

是不是可以創建* view *用戶表,它只是排除鎖定的記錄,並獲得認證工作呢? 'DBIx :: Class :: ResultSource :: View'是'DBIx :: Class :: ResultSource'的一個子類,所以調用的代碼不應該知道任何不同,但你對舞者的瞭解要多於我。 – Borodin

+1

@Borodin這主意聽起來很不錯。我沒有想過。如果這件事情在引擎蓋下使用DBIC。如果沒有,常規觀點也可以發揮作用。我喜歡。 – simbabque

+0

我會試試這個@Borodin。如果能解決問題,我們可以將其發佈到文檔中。 – simbabque