Spring Security自定义UserDetailsService中UserRepo注入为空引发空指针异常
问题解决:CustomUserDetails中UserRepo为Null导致认证失败
问题原因
核心问题是你在Securitybeans中手动new CustomUserDetails()创建实例,这个实例不在Spring容器的管理范围内,Spring无法为它注入UserRepo和PasswordEncoder依赖,所以调用loadUserByUsername时会抛出NullPointerException。
另外,CustomUserDetails中的UserBuilder初始化存在问题(builder为null),即使UserRepo注入成功,也会引发新的NPE。
解决方案步骤
- 让Spring容器管理
CustomUserDetails实例,不再手动new - 修复
UserBuilder的初始化逻辑 - 优化安全配置,确保认证提供者正确关联容器中的组件
修改后的代码
1. 安全Bean配置(Securitybeans.java)
package com.workout.befit.utils; import org.springframework.context.annotation.Bean; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; import com.workout.befit.services.CustomUserDetails; @Component public class Securitybeans { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } // 注入Spring管理的CustomUserDetails实例,而非手动new @Bean public UserDetailsService customuserdetailsservice(CustomUserDetails customUserDetails){ return customUserDetails; } @Bean public AuthenticationProvider authprovider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder){ DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setPasswordEncoder(passwordEncoder); provider.setUserDetailsService(userDetailsService); return provider; } }
2. 自定义UserDetailsService(CustomUserDetails.java)
package com.workout.befit.services; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.User.UserBuilder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import com.workout.befit.models.User; import com.workout.befit.repository.UserRepo; @Service("customuserdetails") public class CustomUserDetails implements UserDetailsService { @Autowired UserRepo userrepo; @Autowired PasswordEncoder bcrypt; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Optional<User> optionaluser = userrepo.findByUsername(username); // 抛出明确的异常信息,便于排查 User user = optionaluser.orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username)); // 正确初始化UserBuilder UserBuilder builder = User.withUsername(user.getUsername()); // 注意:注册时已加密密码,直接使用数据库中的密文即可 builder.password(user.getPassword()); builder.authorities(user.getAuthorities()); return builder.build(); } }
3. 安全配置(SecurityConfig.java)
package com.workout.befit.utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { @Autowired AuthenticationProvider authprovider; @Bean public SecurityFilterChain securityFilterchain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize .requestMatchers("/signup","/about","/error/**").permitAll() .anyRequest().authenticated()) .authenticationProvider(authprovider) // 指定自定义认证提供者 .httpBasic(Customizer.withDefaults()) .formLogin(Customizer.withDefaults()) .csrf(csrf-> csrf.disable()); return http.build(); } }
关键说明
- Spring容器管理实例:只有Spring创建的实例(通过@Service、@Component等注解或@Bean定义)才能享受依赖注入,手动new的对象无法被Spring管理,依赖字段会保持null。
- UserBuilder初始化:必须使用
User.withUsername()或User.builder()创建builder实例,不能直接赋值为null。 - 密码加密规范:用户注册时已通过PasswordEncoder加密密码并存入数据库,认证时无需再次加密,直接传入数据库中的密文,DaoAuthenticationProvider会自动完成明文与密文的匹配。
内容的提问来源于stack exchange,提问作者Fitz chivalry farseer
相关产品推荐
相关产品推荐

