Halo 中实现用户密码的自定义校验扩展

基于 Halo 本身提供了用户密码额外校验实现,基于 UsernamePasswordAuthenticationManager 实现用户账号密码信息的校验。这样我们就可以实现如 LDAP 的用户密码校验

源码分析

UsernamePasswordAuthenticator.java 认证验证类指定验证管理器

ReactiveAuthenticationManager authenticationManager() {
    var manager = new UsernamePasswordDelegatingAuthenticationManager(extensionGetter,
        defaultAuthenticationManager());
    return new ObservationReactiveAuthenticationManager(observationRegistry, manager);
}

UsernamePasswordDelegatingAuthenticationManager.java提供一个 UsernamePasswordAuthenticationManager 的扩展点,如果扩展点返回的 MonoEmpty 则使用 Halo 本身的认证逻辑

@Override
public Mono<Authentication> authenticate(Authentication authentication) {
    return extensionGetter
        .getEnabledExtensionByDefinition(UsernamePasswordAuthenticationManager.class)
        .next()
        .flatMap(authenticationManager -> authenticationManager.authenticate(authentication)
            .doOnError(t -> log.error(
                "failed to authenticate with {}, fallback to default username password "
                    + "authentication.", authenticationManager.getClass(), t)
            )
            .onErrorResume(
                t -> !(t instanceof AuthenticationException),
                t -> Mono.empty()
            )
        )
        .switchIfEmpty(
            Mono.defer(() -> defaultAuthenticationManager.authenticate(authentication))
        );
}

认证实现

在插件中实现 UsernamePasswordAuthenticationManager 通过以下代码获取到用户的账号密码结合业务自身的逻辑返回对应的Authentication即可,如果有异常需要基于AuthenticationException进行抛出。

@Component
@RequiredArgsConstructor
@Slf4j
public class CustomAuthenticationManager implements UsernamePasswordAuthenticationManager {

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        // 用户输入的用户
        String userName = authentication.getName();
        // 用户输入的密码
        String password = String.valueOf(authentication.getCredentials());
        //构建 userdetails 需要按照自己的逻辑去校验,如果失败就报错即可 需要抛出AuthenticationException
        UserDetails userDetails = User.builder()
            .username(userName)
            .password(password)
            .roles("super-role")
            .build();
        // 构建 Authentication
        UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(userDetails,
            userDetails.getPassword(),
            userDetails.getAuthorities());
        return Mono.just(token);
    }
}