如何使用 Dancer2::Plugin::Auth::Extensible 处理 locked/disabled 用户帐户?
How to handle locked/disabled user accounts with Dancer2::Plugin::Auth::Extensible?
我目前正在将 CGI 应用程序迁移到 Dancer2。我以前使用 "hand-crafted" 身份验证机制,使用 MySQL 和具有属性 email
、password
和 state
的用户 table。 state
表示该帐户是 active
还是 locked
。 locked
表示该帐户已被禁用(逻辑删除)。
我还有 tables roles
和 user_roles
来实现我的两个角色:管理员和用户。
一切都像一个魅力,有一个例外:
使用我的旧 "hand-crafted" 机制,我能够 锁定 用户,即逻辑上删除它们而不将它们从数据库中删除。仅当电子邮件和 hash_of(密码)匹配 且 帐户未被锁定时,登录才成功。
如何使用 Dancer2::Plugin::Auth::Extensible
and Dancer2::Plugin::Auth::Extensible::Provider::Database
实现它?
我希望钩子after_authenticate_user
可以returntrue
或false
覆盖authenticate_user
的结果,但事实并非如此。至少,它没有记录。
我想到的一件事是有一个额外的角色 active
然后 - 对于每条路线 - require_role active
而不是 require_login
.
所以我的问题是:如何让 Dancer2::Plugin::Auth::Extensible
只考虑 active
用户?
您可以子class Dancer2::Plugin::Auth::Extensible::Provider::Database 并包装get_user_details
方法来检查用户是否处于活动状态。
考虑一下我使用的同一个应用程序 。添加以下 class.
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 data with the get_user_details
method, so we can hook in there. Our around
wrapper将透明地注入用户活跃度检查,其余代码甚至不知道有什么区别。
非活跃用户不会出现在与 Plugin::Auth::Extensible 相关的任何互动中。
创建一个视图并将其作为用户 table 使用。我已经做了一些测试,可以说这确实是实现这一目标的最简单方法。
警告:由于视图的性质,这使得应用程序无法修改或添加用户!
考虑以下 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
table 包含插件建议的默认列,外加一列 disabled
,可以是 NULL
或时间戳。我认为用 disabled 比用 active.
更容易说明
然后我对 lib/Foo.pm
进行了以下更改。所有这些基本上来自 Dancer2::Plugin::Auth::Extensible and Dancer2::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。您会看到默认的登录表单。
输入 foo
和 test
。这应该有效,您应该会看到 /users
路由。 (或者没有,就像我的情况一样,重定向似乎被破坏了......)。
现在转到 http://localhost:5000/logout 删除 foo 的 cookie 并再次打开 http://localhost:5000/users。这次,输入 bar
和 test
.
您会看到登录不起作用。
要进行反测,请替换config.yml
中的users_table
并重启应用程序。
# config.yml
users_table: 'users'
现在用户 foo
将能够登录。
这种方法不仅易于实现,而且应该是迄今为止性能最高的方法,因为数据库处理所有逻辑(并且很可能已经缓存了它)。
您的应用程序,尤其是身份验证插件,根本不需要知道 active 或 disabled 字段的存在.他们不需要关心。东西会正常工作。
我目前正在将 CGI 应用程序迁移到 Dancer2。我以前使用 "hand-crafted" 身份验证机制,使用 MySQL 和具有属性 email
、password
和 state
的用户 table。 state
表示该帐户是 active
还是 locked
。 locked
表示该帐户已被禁用(逻辑删除)。
我还有 tables roles
和 user_roles
来实现我的两个角色:管理员和用户。
一切都像一个魅力,有一个例外:
使用我的旧 "hand-crafted" 机制,我能够 锁定 用户,即逻辑上删除它们而不将它们从数据库中删除。仅当电子邮件和 hash_of(密码)匹配 且 帐户未被锁定时,登录才成功。
如何使用 Dancer2::Plugin::Auth::Extensible
and Dancer2::Plugin::Auth::Extensible::Provider::Database
实现它?
我希望钩子after_authenticate_user
可以returntrue
或false
覆盖authenticate_user
的结果,但事实并非如此。至少,它没有记录。
我想到的一件事是有一个额外的角色 active
然后 - 对于每条路线 - require_role active
而不是 require_login
.
所以我的问题是:如何让 Dancer2::Plugin::Auth::Extensible
只考虑 active
用户?
您可以子class Dancer2::Plugin::Auth::Extensible::Provider::Database 并包装get_user_details
方法来检查用户是否处于活动状态。
考虑一下我使用的同一个应用程序
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 data with the get_user_details
method, so we can hook in there. Our around
wrapper将透明地注入用户活跃度检查,其余代码甚至不知道有什么区别。
非活跃用户不会出现在与 Plugin::Auth::Extensible 相关的任何互动中。
警告:由于视图的性质,这使得应用程序无法修改或添加用户!
考虑以下 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
table 包含插件建议的默认列,外加一列 disabled
,可以是 NULL
或时间戳。我认为用 disabled 比用 active.
然后我对 lib/Foo.pm
进行了以下更改。所有这些基本上来自 Dancer2::Plugin::Auth::Extensible and Dancer2::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。您会看到默认的登录表单。
输入 foo
和 test
。这应该有效,您应该会看到 /users
路由。 (或者没有,就像我的情况一样,重定向似乎被破坏了......)。
现在转到 http://localhost:5000/logout 删除 foo 的 cookie 并再次打开 http://localhost:5000/users。这次,输入 bar
和 test
.
您会看到登录不起作用。
要进行反测,请替换config.yml
中的users_table
并重启应用程序。
# config.yml
users_table: 'users'
现在用户 foo
将能够登录。
这种方法不仅易于实现,而且应该是迄今为止性能最高的方法,因为数据库处理所有逻辑(并且很可能已经缓存了它)。
您的应用程序,尤其是身份验证插件,根本不需要知道 active 或 disabled 字段的存在.他们不需要关心。东西会正常工作。