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

Spring Security中UserRepository在UserDetailsService返回null但其他类可用

问题描述

我的Spring Boot应用可正常添加用户,但登录时,loadUserByUsername方法能接收到传入的username,不过调用UserRepository.getUserName(username)时返回null,而该UserRepository在MainController中可正常查询数据库获取用户数据。我作为Spring Security新手,使用MySQL数据库实现登录验证,已尝试多日仍未解决,相关代码如下:


相关代码

Userdetailserviceimplmn

package com.ToDoList.Configuration;
import org.springframework.beans.factory.annotation.Autowired;
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.ToDoList.Entity.User;
import com.ToDoList.Repository.UserRepository;

import jakarta.transaction.Transactional;

@Service
public class Userdetailserviceimplmn implements UserDetailsService {
    
    @Autowired
     UserRepository  userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("User details ......");
        System.out.println(username + "......");
        User userxyz = userRepository.getUserName(username);
        // 此处返回null,但在其他类中可正常查询
        System.out.println(userxyz);
        
        if(userxyz == null) {
            throw new UsernameNotFoundException("Could not found user !!!");
        }
        
        userdetailsservice userdetailservice = new userdetailsservice(userxyz);
        return userdetailservice;
    }
}

userdetailsservice

package com.ToDoList.Configuration;

import java.util.Collection;
import java.util.HashSet;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.ToDoList.Entity.User;

public class userdetailsservice implements UserDetails {
    
    private User user;
    
    public userdetailsservice(User user) {
        super();
        this.user=user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
         return new HashSet<GrantedAuthority>();
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getEmail();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

WebConfig

package com.ToDoList.Configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class WebConfig{
    
    @Autowired
    private Userdetailserviceimplmn usercustom;
    
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    public UserDetailsService getUserService() {
        return new Userdetailserviceimplmn();
    }
     
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
        System.out.println("AuthenticationManagerBuilder");
        System.out.println(usercustom);
        auth.userDetailsService(usercustom).passwordEncoder(passwordEncoder());
    } 
    
    @Bean
    public DaoAuthenticationProvider authProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(getUserService());
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }
    
    @Bean 
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
        http.csrf().disable().authorizeHttpRequests()
            .requestMatchers("/home","/login","/loginvalidation").permitAll()
            .requestMatchers("/tasks/**").authenticated()
            .and().formLogin()
            .loginPage("/login")
            .loginProcessingUrl("/loginvalidation")
            .defaultSuccessUrl("/tasks/dashboards").permitAll();
        
        return http.build();
    }
}

MainController

package com.ToDoList.Controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

import com.ToDoList.Entity.User;
import com.ToDoList.Repository.UserRepository;

@Controller
public class MainController {
    
    @Autowired
    public UserRepository userrepository;
    
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;
    
    @RequestMapping("/home")
    public String HomePage(Model model) {
        model.addAttribute("user",new User());
        System.out.println("Home page....");
        return "home";
    }
    
    @RequestMapping("/login")
    public String Login(Model model) {
        System.out.println("login page..... ");
        return "login";
    }
    
    @RequestMapping("/UserRegistration")
    public String UserRegistration(@ModelAttribute("user") User user,Model model) {
        try {
            System.out.print("user registration ......");
            user.setPassword(passwordEncoder.encode(user.getPassword()));
            userrepository.save(user);
            User usertest = userrepository.getUserName(user.getEmail());
            System.out.print(usertest.toString());
        }
        catch(Exception e){
            e.printStackTrace();
        }
        model.addAttribute("user",new User());
          return "redirect:" + "/home";
    }
}

Tasks.java

package com.ToDoList.Controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.ToDoList.Entity.User;
@Controller
@RequestMapping("/tasks")
public class tasks {
    
    @RequestMapping("/dashboards")
    public String dashboards(Model model) {
        System.out.print("task page   ...." );
        return "tasks";
    }
}

Entity: Tasks.java

package com.ToDoList.Entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;

@Entity
@Table(name = "Tasks")
public class Tasks {
    
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name = "id")
    private int id;
    
    @Column(name = "Heading",nullable = false)
    private String Heading;
    
    @Column(name = "Description",nullable = false)
    private String Description;
    
    @Column(name = "Date",nullable = false)
    private String Date;
    
    @ManyToOne
    private User user;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getHeading() {
        return Heading;
    }

    public void setHeading(String heading) {
        Heading = heading;
    }

    public String getDescription() {
        return Description;
    }

    public void setDescription(String description) {
        Description = description;
    }

    public String getDate() {
        return Date;
    }

    public void setDate(String date) {
        Date = date;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Tasks [id=" + id + ", Heading=" + Heading + ", Description=" + Description + ", Date=" + Date
                + ", user=" + user + ", getId()=" + getId() + ", getHeading()=" + getHeading() + ", getDescription()="
                + getDescription() + ", getDate()=" + getDate() + ", getUser()=" + getUser() + ", getClass()="
                + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString() + "]";
    }
}

Entity: User.java

package com.ToDoList.Entity;

import java.util.List;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;

@Entity
@Table(name = "User")
public class User {
    
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Id 
    private int SerialNo;
    
    @Column(name = "Email")
    private String Email;
    
    @Column(name = "firstName")
    private String firstName;
    
    @Column(name = "LastName")
    private String LastName;
    
    @Column(name = "Description")
    private String Description;
    
    @Column(name = "Goal")
    private String Goal;
    
    @Column(name = "Password")
    private String Password;
    
    @OneToMany(mappedBy="id",cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Tasks> Tasks;

    public int getSerialNo() {
        return SerialNo;
    }

    public void setSerialNo(int serialNo) {
        SerialNo = serialNo;
    }

    public String getEmail() {
        return Email;
    }

    public void setEmail(String email) {
        Email = email;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return LastName;
    }

    public void setLastName(String lastName) {
        LastName = lastName;
    }

    public String getDescription() {
        return Description;
    }

    public void setDescription(String description) {
        Description = description;
    }

    public String getGoal() {
        return Goal;
    }

    public void setGoal(String goal) {
        Goal = goal;
    }

    public String getPassword() {
        return Password;
    }

    public void setPassword(String password) {
        Password = password;
    }

    public List<Tasks> getTasks() {
        return Tasks;
    }

    public void setTasks(List<Tasks> tasks) {
        Tasks = tasks;
    }

    @Override
    public String toString() {
        return "User [SerialNo=" + SerialNo + ", Email=" + Email + ", firstName=" + firstName + ", LastName=" + LastName
                + ", Description=" + Description + ", Goal=" + Goal + ", Password=" + Password + ", Tasks=" + Tasks
                + "]";
    }   
}

UserRepository

package com.ToDoList.Repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import com.ToDoList.Entity.User;

@Repository
public interface UserRepository extends JpaRepository<User,String> {
    
    @Query("SELECT u FROM User u  WHERE u.Email=:Email")
    User getUserName(@Param("Email") String Email);
}

问题修复方案

1. 修正UserRepository泛型参数

User实体的主键类型是int(SerialNo字段),但当前继承的JpaRepository<User, String>泛型不匹配,改为:

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
    @Query("SELECT u FROM User u WHERE u.Email=:Email")
    User getUserName(@Param("Email") String Email);
}

2. 修复WebConfig中UserDetailsService实例重复创建问题

你手动new的Userdetailserviceimplmn实例不受Spring管理,导致依赖注入失效,同时Spring Security 5.7+已弃用WebSecurityConfigurerAdapter,原configure方法不会被执行。修正WebConfig:

@Configuration
@EnableWebSecurity
public class WebConfig{
    
    @Autowired
    private Userdetailserviceimplmn usercustom;
    
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;
    
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    public DaoAuthenticationProvider authProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(usercustom); // 使用Spring注入的实例
        authProvider.setPasswordEncoder(passwordEncoder);
        return authProvider;
    }
    
    @Bean 
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
        http.csrf().disable()
            .authenticationProvider(authProvider()) // 添加自定义认证提供者
            .authorizeHttpRequests()
                .requestMatchers("/home","/login","/loginvalidation").permitAll()
                .requestMatchers("/tasks/**").authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/loginvalidation")
                .defaultSuccessUrl("/tasks/dashboards")
                .permitAll();
        
        return http.build();
    }
}

3. 修正User实体OneToMany映射

User类中@OneToMany(mappedBy="id")错误,应对应Tasks类中的user字段:

@OneToMany(mappedBy="user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Tasks> Tasks;

4. 为loadUserByUsername添加事务注解

确保数据库查询在事务上下文执行:

@Override
@Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    // 原有代码不变
}

5. 检查登录表单参数名

确保登录表单中用户名输入框的name属性为username,如果用邮箱作为用户名,可在WebConfig中配置:

.formLogin()
    .loginPage("/login")
    .usernameParameter("email") // 对应表单输入框name属性
    .loginProcessingUrl("/loginvalidation")
    .defaultSuccessUrl("/tasks/dashboards")
    .permitAll();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 13:05:56