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

Spring Security自定义UserDetailsService中UserRepo注入为空引发空指针异常

问题解决:CustomUserDetails中UserRepo为Null导致认证失败

问题原因

核心问题是你在Securitybeans中手动new CustomUserDetails()创建实例,这个实例不在Spring容器的管理范围内,Spring无法为它注入UserRepo和PasswordEncoder依赖,所以调用loadUserByUsername时会抛出NullPointerException。

另外,CustomUserDetails中的UserBuilder初始化存在问题(builder为null),即使UserRepo注入成功,也会引发新的NPE。

解决方案步骤

  1. 让Spring容器管理CustomUserDetails实例,不再手动new
  2. 修复UserBuilder的初始化逻辑
  3. 优化安全配置,确保认证提供者正确关联容器中的组件

修改后的代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 21:34:51