You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Micronaut框架中PasswordEncoder实例化失败问题求助

问题

作为Micronaut框架新手,在实现用户注册功能时,将AuthenticationProvider逻辑抽离到AuthService后,发起注册POST请求时出现Bean实例化错误,核心异常为java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory,根源是Spring Security的BCryptPasswordEncoder依赖的日志类缺失。

相关代码如下:

UserAuthProvider

@Singleton
public class UserAuthProvider implements AuthenticationProvider {
    private final AuthService authService;

    public UserAuthProvider(AuthService authService) {
        this.authService = authService;
    }

    @Override
    public Publisher<AuthenticationResponse> authenticate(HttpRequest<?> httpRequest, AuthenticationRequest<?, ?> authenticationRequest) {
        return Flux.create(emitter -> {
            var username = authenticationRequest.getIdentity().toString();
            var password = authenticationRequest.getSecret().toString();

            var user = this.authService.getValidatedUser(username, password);
            if (user != null) {
                emitter.next(AuthenticationResponse.success(username, List.of(user.roleId().toString())));
                emitter.complete();
            } else  {
                emitter.error(AuthenticationResponse.exception());
            }
        }, FluxSink.OverflowStrategy.ERROR);
    }
}

AuthService

@Singleton
public class AuthService {
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final RoleRepository roleRepository;

    public AuthService(UserRepository userRepository, PasswordEncoder passwordEncoder, RoleRepository roleRepository) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
        this.roleRepository = roleRepository;
    }

    public void register(RegistrationRequest registrationRequest) {
        var user = this.userRepository.findByEmailEmail(registrationRequest.email());
        if (user.isPresent()) {
            throw new EntityAlreadyExistsException(String.format(EMAIL_ALREADY_EXISTS,registrationRequest.email()));
        }

        var role = this.roleRepository.findByName(RoleEnum.ROLE_PARENT)
                .orElseThrow(() -> new EntityNotFoundException(ENTITY_NOT_FOUND));

        var newUser = new ApplicationUser(
                new Email(registrationRequest.email()),
                this.passwordEncoder.encode(registrationRequest.password()),
                registrationRequest.firstName(),
                registrationRequest.middleName(),
                registrationRequest.lastName(),
                new MobilePhone(registrationRequest.phoneNumber()),
                role
        );

        var patient = new Parent();
        newUser.setParent(patient);

        this.userRepository.save(newUser);
    }

    public UserDto getValidatedUser(String username, String password) {
        if (this.validateCredentials(username, password)) {
            return this.findByEmail(username);
        }

        return null;
    }

    private boolean validateCredentials(String username, String password) {
        var user = this.userRepository.findByEmailEmail(username)
                .orElseThrow(() -> new EntityNotFoundException(ENTITY_NOT_FOUND));

        return this.passwordEncoder.matches(password, user.getPassword());
    }

    public UserDto findByEmail(String email) {
        return this.userRepository.findByEmailEmail(email)
                .map(u -> new UserDto(
                        u.getId(),
                        u.getFirstName(),
                        u.getLastName(),
                        u.getEmail().getEmail(),
                        u.getRole().getId()))
                .orElseThrow(() -> new EntityNotFoundException(ENTITY_NOT_FOUND));
    }
}

AuthPasswordEncoderService

package server.infrastructure.config.encoding;

import jakarta.inject.Singleton;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Singleton
public class AuthPasswordEncoderService implements PasswordEncoder {
    PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    @Override
    public String encode(CharSequence rawPassword) {
        return this.passwordEncoder.encode(rawPassword);
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return this.passwordEncoder.matches(rawPassword, encodedPassword);
    }
}

错误核心堆栈:

Caused by: java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
at org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder.<init>(BCryptPasswordEncoder.java:40)
at server.infrastructure.config.encoding.AuthPasswordEncoderService.<init>(AuthPasswordEncoderService.java:9)
...
Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
解决方案

方案一:补充缺失的Commons Logging依赖

Spring Security的BCryptPasswordEncoder依赖Apache Commons Logging组件,需在构建文件中添加该依赖:

Gradle(Groovy)

implementation 'commons-logging:commons-logging:1.2'

Maven

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>

方案二:改用Micronaut原生PasswordEncoder(推荐)

Micronaut Security自带BCrypt密码编码实现,无需依赖Spring Security组件,更适配框架生态:

  1. 确保引入Micronaut Security Password依赖:

Gradle(Groovy)

implementation("io.micronaut.security:micronaut-security-password")
  1. 修改AuthPasswordEncoderService代码:
package server.infrastructure.config.encoding;

import jakarta.inject.Singleton;
import io.micronaut.security.authentication.password.PasswordEncoder;
import io.micronaut.security.password.bcrypt.BCryptPasswordEncoder;
import io.micronaut.security.password.PasswordEncoderConfiguration;

@Singleton
public class AuthPasswordEncoderService implements PasswordEncoder {
    private final PasswordEncoder passwordEncoder;

    public AuthPasswordEncoderService(PasswordEncoderConfiguration configuration) {
        this.passwordEncoder = new BCryptPasswordEncoder(configuration);
    }

    @Override
    public String encode(CharSequence rawPassword) {
        return passwordEncoder.encode(rawPassword);
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }
}

内容的提问来源于stack exchange,提问作者AchoVasilev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 19:24:59