是否可以在Micronaut中定义多个authenticationProviders
?
假设我有一个实体A ,可以使用 authenticationProviderA 进行记录:该实体给了用户并通过了A的 DB表
是否可以添加一个实体B 及其 authenticationProviderB ,该实体授予用户并通过,将检查B的 DB表?
如果是这样,您如何在控制器中定义要使用哪个authenticationProvider
?
答案 0 :(得分:0)
看过io.micronaut.security.authentication.Authenticator
后,我发现Micronaut中可能有多个authenticationProviders
。
文档说:
Authenticator对多个
{@link AuthenticationProvider}
实例进行操作,并返回第一个经过验证的{@link AuthenticationResponse}
。
根据我所看到的,您只需要实现AuthenticationProvider
,并且Authenticator
将在AuthenticationProviders
的内部列表中包含这些实现(即使没有注释!)。
恕我直言,这不是提供多种身份验证方式的好方法。在问题提供的示例中,对A和B的身份验证都需要对DB的调用,这意味着将根据AuthenticationProviders
不需要的BD调用的执行顺序来执行。
我认为最好提供一种方法来指示控制器或端点必须使用哪个AuthenticationProviders
。
也许有一种方法可以做到这一点,但我只是不知道,因此可以随意发表评论。
答案 1 :(得分:0)
没有针对该问题的内置解决方案,但有多种方法可以用少量代码实现您想要的效果。
解决方案一:
如果您需要多个登录端点,请创建自定义 AuthenticationRequest.class
和 LoginController.class
:
public class AuthenticationRequestForEntityA extends UsernamePasswordCredentials { ... }
public class AuthenticationRequestForEntityB extends UsernamePasswordCredentials { ... }
在您的自定义 LoginController
中,将默认 UsernamePasswordCredentials
替换为您的特定 AuthenticationRequestForEntityA
或 AuthenticationRequestForEntityB
,并复制粘贴原始 LoginController.class
中的其余代码:
@Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON})
@Post
public Single<MutableHttpResponse<?>> login(@Valid @Body AuthenticationRequestForEntityA usernamePasswordCredentials, HttpRequest<?> request) {
Flowable<AuthenticationResponse> authenticationResponseFlowable = Flowable.fromPublisher(authenticator.authenticate(request, usernamePasswordCredentials));
...
然后在您的身份验证提供程序中:
public class AuthenticationProviderA implements AuthenticationProvider {
@Override
public Publisher<AuthenticationResponse> authenticate(@Nullable HttpRequest<?> httpRequest, AuthenticationRequest<?, ?> authenticationRequest) {
if (authenticationRequest instanceof AuthenticationRequestForEntityA) {
return authenticate(authenticationRequest);
} else {
// return empty
}
}
}
public class AuthenticationProviderB implements AuthenticationProvider {
@Override
public Publisher<AuthenticationResponse> authenticate(@Nullable HttpRequest<?> httpRequest, AuthenticationRequest<?, ?> authenticationRequest) {
if (authenticationRequest instanceof AuthenticationRequestForEntityB) {
return authenticate(authenticationRequest);
} else {
// return empty
}
}
}
解决方案 №2:创建自定义的基于路由的 AuthenticationProvider
由于 HttpRequest
在 AuthenticationProvider
中可用作输入参数,因此您可以简单地根据 httpRequest
路径或查询参数属性进行身份验证。
为了使代码更简洁,您可以创建自己的 RouteBasedAuthenticationProvider
界面:
public interface RequestBasedAuthenticationProvider extends AuthenticationProvider {
/**
You can check the request path or request parameter or whatever
*/
boolean supports(HttpRequest<?> request);
}
然后在 Micronaut AuthenticationProvider
中:
@Context
public class AppAuthenticationProvider implements AuthenticationProvider {
private final Collection<RequestBasedAuthenticationProvider> providers;
constructor(...) {...}
@Override
public Publisher<AuthenticationResponse> authenticate(@Nullable HttpRequest<?> httpRequest, AuthenticationRequest<?, ?> authenticationRequest) {
return providers.stream()
.filter(provider -> provider.supports(httpRequest))
.findFirst()
.orElseThrow(//Throw provider not found error)
.authenticate(httpRequest, authenticationRequest);
}
}