Micronaut中的多重身份验证提供程序

时间:2020-06-01 10:58:33

标签: java micronaut

是否可以在Micronaut中定义多个authenticationProviders

假设我有一个实体A ,可以使用 authenticationProviderA 进行记录:该实体给了用户并通过了A的 DB表

是否可以添加一个实体B 及其 authenticationProviderB ,该实体授予用户并通过,将检查B的 DB表? 如果是这样,您如何在控制器中定义要使用哪个authenticationProvider

2 个答案:

答案 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.classLoginController.class

public class AuthenticationRequestForEntityA extends UsernamePasswordCredentials { ... }

public class AuthenticationRequestForEntityB extends UsernamePasswordCredentials { ... }

在您的自定义 LoginController 中,将默认 UsernamePasswordCredentials 替换为您的特定 AuthenticationRequestForEntityAAuthenticationRequestForEntityB,并复制粘贴原始 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

由于 HttpRequestAuthenticationProvider 中可用作输入参数,因此您可以简单地根据 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);

    }

}