Dart Shelf的认证和授权中间件
介绍
提供Shelf中间件,用于验证用户(或系统)和建立会话,以及授权访问资源。
用法
认证
注意:有关构建身份验证中间件的替代方法,请参阅下面的“身份验证生成器”部分。
var authMiddleware = authenticate([
new BasicAuthenticator(new TestLookup()),
new RandomAuthenticator()]));
Shelf Auth提供了一个authenicate函数,它接受一个Authenticators列表和一个可选的SessionHandler(见下文)并创建Shelf Middleware。
然后,您可以在shelf pipeline中的适当位置添加此Middleware.
var handler = const Pipeline()
.addMiddleware(exceptionHandler())
.addMiddleware(authMiddleware)
.addHandler((Request request) => new Response.ok("I'm in with "
"${getAuthenticatedContext(request).map((ac) => ac.principal.name)}\n"));
io.serve(handler, 'localhost', 8080);
调用身份验证中间件时,它会按顺序通过身份验证器。 每个Authenticator都执行以下操作之一
- 返回表示身份验证成功的结果(带有上下文)
- 返回一个表明身份验证者没有找到任何与之相关的凭据结果
- 抛出一个异常,表明验证器确实找到了相关的凭据,但认为用户不应该登录
第一个Authenticator返回成功身份验证或抛出异常。 如果Authenticator指示它未找到相关凭据,则调用列表中的下一个验证器。
如果没有抛出异常,那么将调用传递给中间件的innerHandler。如果身份验证成功,则请求将在请求上下文中包含与身份验证相关的数据。这可以通过getAuthenticatedContext函数从当前请求中检索,也可以通过authenticatedContext从当前区域中检索。
成功的认证会创建新区域(将经过身份验证的上下文设置为区域变量)。 可以使用authenticatedContext函数访问它。
如果没有任何验证器处理请求,则调用innerHandler而不使用任何验证上下文。下游处理程序应该将其视为未经身份验证的(来宾)用户访问。您可以通过使用allowAnonymousAccess:false调用authenticate函数来拒绝匿名访问。
在登录时建立会话
如果没有为authenticate函数提供SesionHandler,则不会建立任何会话。 这意味着每个请求都需要进行身份验证。 这适用于系统到系统调用以及基本身份验证等身份验证机制。
要在成功登录时创建会话,还包括SessionHandler
var authMiddleware = authenticate([new RandomAuthenticator()],
new JwtSessionHandler('super app', 'shhh secret', testLookup));
如果生成的AuthenticatedContext支持会话,则将在成功验证时调用SessionHandler。
请注意,除了指示身份验证是否成功之外,Authenticators还指示是否允许创建会话。对于某些认证机制(例如服务器到服务器调用),可能不希望创建会话。
SessionHandlers提供了一个Authenticator,它始终是第一个为请求调用的身份验证器。只有在没有活动会话时才会调用其他身份验证器。
请注意,Shelf Auth不会涵盖session属性的存储(添加/检索)。这超出了范围。 只有会话处理的身份验证相关部分才在范围内。任何支持Shelf Auth标头或可与其集成的会话存储库都可以使用Shelf Auth。
验证器
Shelf Auth提供以下开箱即用的验证器:
BasicAuthenticator
支持基本身份验证(http://tools.ietf.org/html/rfc2617)
默认情况下,BasicAuthenticator不支持会话创建。 这可以在创建验证器时重写,如下所示
new BasicAuthenticator(new TestLookup(), sessionCreationAllowed: true)
UsernamePasswordAuthenticator
用于专用登录路由的Authenticator。 默认情况下,假定基于表单的POST使用名为username和password的表单字段,例如。
curl -i -H 'contentType: application/x-www-form-urlencoded' -X POST -d 'username=fred&password=blah' http://localhost:8080/login
这种身份验证方式几乎总是与建立会话相关联。
var loginMiddleware = authenticate(
[new UsernamePasswordAuthenticator(lookupByUsernamePassword)],
sessionHandler: sessionHandler);
您可以设置登录路由(在此示例中使用shelf_route)并传入此中间件。
rootRouter.post('/login', (Request request) => new Response.ok(
"I'm now logged in as ${loggedInUsername(request)}\n"),
middleware: loginMiddleware);
现在,您通常会设置通过登录时建立的会话访问的其他路由。
var defaultAuthMiddleware = authenticate([],
sessionHandler: sessionHandler, allowHttp: true,
allowAnonymousAccess: false);
rootRouter.child('/authenticated', middleware: defaultAuthMiddleware)
..get('/foo', (Request request) => new Response.ok(
"Doing foo as ${loggedInUsername(request)}\n"));
在此示例中,以/ authenticated开头的所有路由都需要有效的会话。
认证人员名单预计会随着时间的推移而增长。
此外,您可以轻松创建自己的自定义身份验证器。
Session Handlers
Shelf Auth提供以下开箱即用的SessionHandler:
JwtSessionHandler
这使用JWT创建在响应的Authorization标头中返回的身份验证令牌。后续请求必须在Authorization标头中传回令牌。这是一种承载风格的令牌机制。注意:与HTTP消息中传递的所有安全凭证一样,如果有人能够拦截请求或响应,则他们可以窃取令牌并模拟用户。确保使用HTTPS。
特征
- 不需要在服务器上存储任何东西来支持会话。 任何有权访问用于创建令牌的秘密的服务器进程都可以对其进行验证。
- 支持非活动超时和总会话超时
其他会话处理程序(如基于cookie的机制)可能会在未来添加
Authentication Builder
为了简化创建身份验证中间件的过程,特别是在使用捆绑的身份验证器和会话处理程序时,会提供构建器。例如
var authMiddleware = (builder()
.basic(userNamePasswordLookup,
sessionCreationAllowed: true)
.jwtSession('me', 'sshh', usernameLookup)
..allowHttp=true)
.build();
注意:此示例有点复杂,因为您通常不希望使用基本身份验证创建会话
授权
var authorisationMiddleware = authorise([new SameOriginAuthoriser()]);
Shelf Auth提供authorise函数,该功能获取Authoriser列表并创建Shelf Middleware。
此外,authorisationBuilder还提供了一个用于创建授权中间件的构建器,包括开箱即用的授权者
var authorisationMiddleware = (authorisationBuilder()
.sameOrigin()
.principalWhitelist((Principal p) => p.name == 'fred'))
.build();
如果任何授权人(Authoriser)拒绝访问,则:
- 如果有经过身份验证的用户,则抛出ForbiddenException
- 否则抛出UnauthorizedException。
Authorisers
Shelf Auth提供以下授权商:
AuthenticatedOnlyAuthoriser
仅允许访问经过身份验证的用户。 如果请求中没有当前的AuthenticatedContext,则拒绝访问。
SameOriginAuthoriser
通过拒绝访问引用不是来自与请求URL相同的主机的请求来帮助防止XSRF攻击。
PrincipalWhitelistAuthoriser
允许访问作为给定PrincipalWhiteList一部分的任何主体的授权者。
PrincipalWhiteList可以以多种方式实现。 例如,它根据名称中的静态内存列表检查主体名称。
final whitelist = [ 'fredlintstone@stone.age' ];
final whitelistAuthoriser = new PrincipalWhitelistAuthoriser(
(Principal p) => whitelist.contains(p.name));
或者它可能会针对数据库检查用户组。