如何使用Dancer2 :: Plugin :: Auth :: Extensible处理锁定/禁用的用户帐户?
我目前正在将CGI应用程序迁移到Dancer2。我以前使用了使用MySQL的“手工制作”身份验证机制,并使用了属性为email
,password
和state
的用户表。 state
表示账户是active
还是locked
。 locked
表示帐户被禁用(逻辑删除)。如何使用Dancer2 :: Plugin :: Auth :: Extensible处理锁定/禁用的用户帐户?
我也有表roles
和user_roles
来实现我的两个角色:管理员和用户。
一切工作就像一个魅力,但有一个例外:
用我的旧的“手工制作”的机制,我能够锁用户,即逻辑删除而不从数据库中删除它们。只有当电子邮件和hash_of(密码)匹配和帐户未被锁定时,登录才会成功。
如何使用Dancer2::Plugin::Auth::Extensible
和Dancer2::Plugin::Auth::Extensible::Provider::Database
来实现?
我希望钩after_authenticate_user
可以返回true
或false
覆盖的authenticate_user
的结果,但事实并非如此。至少,它没有记录。
我想到的一件事是有一个额外的角色active
,然后 - 对于每个路线 - require_role active
而不是只有require_login
。
所以我的问题是:我如何才能使Dancer2::Plugin::Auth::Extensible
只考虑active
用户?
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::Extensible和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的饼干和开放http://localhost:5000/users一次。这次输入bar
和test
。
你会看到登录不起作用。
要反测试,更换config.yml
的users_table
并重新启动应用程序。
# config.yml
users_table: 'users'
现在用户foo
将能够登录。因为数据库处理所有的逻辑(并且很可能已经缓存了它),所以这种方法不仅易于实现,而且也应该是具有最高性能的方式。
您的应用程序,特别是身份验证插件,也不需要了解活跃或禁用领域都存在。他们不需要关心。东西只会工作。
您可能希望做一些奇特的事情来允许活动的用户名与重复的禁用用户名重复。也许是对多列的限制? 'CONSTRAINT unique_name UNIQUE(username,disabled)'可以工作,但不是很漂亮。 – Borodin
@Borodin我不确定这是否是一个好主意。那么用户不再是唯一的。对于审计而言,这可能是相关的。 – simbabque
哇!谢谢你的两个很好的答案。你甚至提交了D2的FAQ。我认为作为第一个镜头,我将在视图中使用该解决方案,因为这更容易实施。从长远来看,我可能会深入挖掘并编写自己的Provider,它完全集成了'state'列。我的应用程序也有一个可以锁定和解锁用户的管理页面,所以如果['update_user'](https://metacpan.org/pod/Dancer2::Plugin::Auth::Extensible# update_user)函数可以无缝工作(包括'state'列)。 – PerlDuck
您可以继承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 data与the get_user_details
method,所以我们可以在那里挂钩。我们的around
wrapper将透明地注入用户活跃性的检查,而其余的代码甚至不知道有什么区别。
非活动用户不会出现在与Plugin :: Auth :: Extensible相关的任何交互中。
我不明白为什么有人会低估你的问题。它写得很好,显示了一些研究(我们认为阅读源不应该被期望,或根本没有问题),它清楚地说明你想要做什么以及为什么,并且有一个代码示例是这里没有关系。 – simbabque
我创建了一个票据将其移入文档:https://github.com/PerlDancer/Dancer2-Plugin-Auth-Extensible-Provider-Database/issues/5 – simbabque