使用 Cro 在 perl6 中进行基本身份验证
Basic Authentication in perl6 with Cro
我正在寻找一种简单的解决方案,通过 Cro 的基本身份验证机制来保护我的路由。在我的示例中,如果您根本不提供任何凭据,我希望看到 401 Unauthorized
。如果您提供了错误的凭据,我希望看到 403 Forbidden
在我的代码示例中,我从未看到 MyBasicAuth
中间件被调用:
class MyUser does Cro::HTTP::Auth {
has $.username;
}
subset LoggedInUser of MyUser where { .username.defined }
class MyBasicAuth does Cro::HTTP::Auth::Basic[MyUser, "username"] {
method authenticate(Str $user, Str $pass --> Bool) {
# No, don't actually do this!
say "authentication called";
my $success = $user eq 'admin' && $pass eq 'secret';
forbidden without $success;
return $success
}
}
sub routes() is export {
my %storage;
route {
before MyBasicAuth.new;
post -> LoggedInUser $user, 'api' {
request-body -> %json-object {
my $uuid = UUID.new(:version(4));
%storage{$uuid} = %json-object;
created "api/$uuid", 'application/json', %json-object;
}
}
}
}
目前 cro 发布版本中似乎存在一个错误,该错误已在 github 的上游修复。在来自 #perl6 IRC 频道的 sena_kun 的帮助下,我们确实使用了当前版本的 cro-http
:
$ git clone https://github.com/croservices/cro-http.git
$ perl6 -Icro-http/lib example.p6
然后 curl
我终于看到了 "authentication called"。
我们又发现了两个小错误:
第一个:当我调用curl -v -d '{"string":"hi"}' http://admin:secret@localhost:10000/api
时忘记添加-H 'Content-Type: application/json'
authentication called An operation first awaited: in sub request-body at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Router.pm6 (Cro::HTTP::Router) line 716 in block at restapp/example.pl line 24 in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Router.pm6 (Cro::HTTP::Router) line 130
Died with the exception:
Cannot unbox a type object (Nil) to int.
in sub decode-payload-to-pairs at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/BodyParsers.pm6 (Cro::HTTP::BodyParsers) line 62
in method parse at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/BodyParsers.pm6 (Cro::HTTP::BodyParsers) line 50
in method body at /home/martin/.rakudobrew/moar-2018.08/install/share/perl6/site/sources/557D6C932894CB1ADE0F83C0596851F9212C2A67 (Cro::MessageWithBody) line 77
in sub request-body at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Router.pm6 (Cro::HTTP::Router) line 716
in block at restapp/example.pl line 24
in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Router.pm6 (Cro::HTTP::Router) line 130
第二个是因为在这部分调用了forbidden
:
class MyBasicAuth does Cro::HTTP::Auth::Basic[MyUser, "username"] {
method authenticate(Str $user, Str $pass --> Bool) {
say "authentication called";
my $success = $user eq 'admin' && $pass eq 'secret';
forbidden unless $success;
return $success;
} }
这导致此堆栈跟踪:
authentication called
Can only use 'content' inside of a request handler
in sub set-status at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Router.pm6 (Cro::HTTP::Router) line 846
in sub forbidden at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Router.pm6 (Cro::HTTP::Router) line 823
in method authenticate at restapp/example.pl line 15
in method process-auth at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Auth/Basic.pm6 (Cro::HTTP::Auth::Basic) line 26
in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Auth/Basic.pm6 (Cro::HTTP::Auth::Basic) line 11
in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Auth/Basic.pm6 (Cro::HTTP::Auth::Basic) line 8
in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Internal.pm6 (Cro::HTTP::Internal) line 22
in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/RequestParser.pm6 (Cro::HTTP::RequestParser) line 109
in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/RequestParser.pm6 (Cro::HTTP::RequestParser) line 90
in block at /home/martin/.rakudobrew/moar-2018.08/install/share/perl6/site/sources/F048BB66854D2463798A39CC2B01D4CC1532F957 (Cro::TCP) line 53
我想这个错误很快就会被修复!
这个结构:
route {
before MyBasicAuth.new;
post -> LoggedInUser $user, 'api' {
...
}
}
取决于即将推出的 Cro 0.8.0 中新的 before
/after
语义。在 asking/writing 时的当前 Cro 版本中 - 以及之前的版本 - route
块中的 before
将仅适用于已经匹配的路由。然而,这对于旨在影响 将 匹配的中间件来说为时已晚。在 Cro 0.8.0 之前执行此操作的方法是在服务器级别安装中间件,或者执行以下操作:
route {
before MyBasicAuth.new;
delegate <*> => route {
post -> LoggedInUser $user, 'api' {
...
}
}
}
这确保在考虑任何路由匹配之前应用中间件。这不是很漂亮,因此即将到来的 0.8.0 中的变化(也将引入具有原始 before
语义的 before-matched
)。
最后,forbidden without $success;
在这里行不通。 forbidden
sub 是 Cro::HTTP::Router
的一部分,用于路由处理程序,而中间件不依赖于路由器(因此您可以决定以不同的方式路由请求,例如,而不会失去能力使用所有的中间件)。 authenticate
方法的约定是它 returns 一个确定应该发生什么的真值;尝试强制使用不同的响应代码不是一个合适的地方。
无法匹配像 LoggedInUser
这样的身份验证约束将产生 401。要重写它,请在最外层的 route
块中添加一个 after
来映射它:
route {
before MyBasicAuth.new;
after { forbidden if response.status == 401; }
delegate <*> => route {
post -> LoggedInUser $user, 'api' {
...
}
}
}
我正在寻找一种简单的解决方案,通过 Cro 的基本身份验证机制来保护我的路由。在我的示例中,如果您根本不提供任何凭据,我希望看到 401 Unauthorized
。如果您提供了错误的凭据,我希望看到 403 Forbidden
在我的代码示例中,我从未看到 MyBasicAuth
中间件被调用:
class MyUser does Cro::HTTP::Auth {
has $.username;
}
subset LoggedInUser of MyUser where { .username.defined }
class MyBasicAuth does Cro::HTTP::Auth::Basic[MyUser, "username"] {
method authenticate(Str $user, Str $pass --> Bool) {
# No, don't actually do this!
say "authentication called";
my $success = $user eq 'admin' && $pass eq 'secret';
forbidden without $success;
return $success
}
}
sub routes() is export {
my %storage;
route {
before MyBasicAuth.new;
post -> LoggedInUser $user, 'api' {
request-body -> %json-object {
my $uuid = UUID.new(:version(4));
%storage{$uuid} = %json-object;
created "api/$uuid", 'application/json', %json-object;
}
}
}
}
目前 cro 发布版本中似乎存在一个错误,该错误已在 github 的上游修复。在来自 #perl6 IRC 频道的 sena_kun 的帮助下,我们确实使用了当前版本的 cro-http
:
$ git clone https://github.com/croservices/cro-http.git
$ perl6 -Icro-http/lib example.p6
然后 curl
我终于看到了 "authentication called"。
我们又发现了两个小错误:
第一个:当我调用curl -v -d '{"string":"hi"}' http://admin:secret@localhost:10000/api
时忘记添加-H 'Content-Type: application/json'
authentication called An operation first awaited: in sub request-body at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Router.pm6 (Cro::HTTP::Router) line 716 in block at restapp/example.pl line 24 in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Router.pm6 (Cro::HTTP::Router) line 130
Died with the exception:
Cannot unbox a type object (Nil) to int.
in sub decode-payload-to-pairs at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/BodyParsers.pm6 (Cro::HTTP::BodyParsers) line 62
in method parse at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/BodyParsers.pm6 (Cro::HTTP::BodyParsers) line 50
in method body at /home/martin/.rakudobrew/moar-2018.08/install/share/perl6/site/sources/557D6C932894CB1ADE0F83C0596851F9212C2A67 (Cro::MessageWithBody) line 77
in sub request-body at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Router.pm6 (Cro::HTTP::Router) line 716
in block at restapp/example.pl line 24
in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Router.pm6 (Cro::HTTP::Router) line 130
第二个是因为在这部分调用了forbidden
:
class MyBasicAuth does Cro::HTTP::Auth::Basic[MyUser, "username"] {
method authenticate(Str $user, Str $pass --> Bool) {
say "authentication called";
my $success = $user eq 'admin' && $pass eq 'secret';
forbidden unless $success;
return $success;
} }
这导致此堆栈跟踪:
authentication called
Can only use 'content' inside of a request handler
in sub set-status at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Router.pm6 (Cro::HTTP::Router) line 846
in sub forbidden at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Router.pm6 (Cro::HTTP::Router) line 823
in method authenticate at restapp/example.pl line 15
in method process-auth at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Auth/Basic.pm6 (Cro::HTTP::Auth::Basic) line 26
in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Auth/Basic.pm6 (Cro::HTTP::Auth::Basic) line 11
in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Auth/Basic.pm6 (Cro::HTTP::Auth::Basic) line 8
in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/Internal.pm6 (Cro::HTTP::Internal) line 22
in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/RequestParser.pm6 (Cro::HTTP::RequestParser) line 109
in block at /home/martin/.workspace/voteimproved/cro-http/lib/Cro/HTTP/RequestParser.pm6 (Cro::HTTP::RequestParser) line 90
in block at /home/martin/.rakudobrew/moar-2018.08/install/share/perl6/site/sources/F048BB66854D2463798A39CC2B01D4CC1532F957 (Cro::TCP) line 53
我想这个错误很快就会被修复!
这个结构:
route {
before MyBasicAuth.new;
post -> LoggedInUser $user, 'api' {
...
}
}
取决于即将推出的 Cro 0.8.0 中新的 before
/after
语义。在 asking/writing 时的当前 Cro 版本中 - 以及之前的版本 - route
块中的 before
将仅适用于已经匹配的路由。然而,这对于旨在影响 将 匹配的中间件来说为时已晚。在 Cro 0.8.0 之前执行此操作的方法是在服务器级别安装中间件,或者执行以下操作:
route {
before MyBasicAuth.new;
delegate <*> => route {
post -> LoggedInUser $user, 'api' {
...
}
}
}
这确保在考虑任何路由匹配之前应用中间件。这不是很漂亮,因此即将到来的 0.8.0 中的变化(也将引入具有原始 before
语义的 before-matched
)。
最后,forbidden without $success;
在这里行不通。 forbidden
sub 是 Cro::HTTP::Router
的一部分,用于路由处理程序,而中间件不依赖于路由器(因此您可以决定以不同的方式路由请求,例如,而不会失去能力使用所有的中间件)。 authenticate
方法的约定是它 returns 一个确定应该发生什么的真值;尝试强制使用不同的响应代码不是一个合适的地方。
无法匹配像 LoggedInUser
这样的身份验证约束将产生 401。要重写它,请在最外层的 route
块中添加一个 after
来映射它:
route {
before MyBasicAuth.new;
after { forbidden if response.status == 401; }
delegate <*> => route {
post -> LoggedInUser $user, 'api' {
...
}
}
}