Java Spring Security不会对新创建的用户进行身份验证,并抛出NoResultException,尽管用户已确认存在于数据库中

Java Spring Security不会对新创建的用户进行身份验证,并抛出NoResultException,尽管用户已确认存在于数据库中,java,spring,spring-mvc,jpa,spring-security,Java,Spring,Spring Mvc,Jpa,Spring Security,整个项目的代码可以在@ 我使用的是SpringSecurity5和SpringMVC项目,它由一个类an支持的自定义dao身份验证 实现UserDetailsService,即PersonService。这些实体是Person、Student以及一些与此处无关的其他实体。这些实体是使用sql脚本初始化的,使用这些脚本初始化的实体使用Spring security登录可以正常工作 但是,我添加了保存新的学生并将其存储在H2数据库中的功能。当我将它们保存在数据库中,然后返回并尝试使用新创建的Stud

整个项目的代码可以在@

我使用的是SpringSecurity5和SpringMVC项目,它由一个类an支持的自定义dao身份验证 实现
UserDetailsService
,即
PersonService
。这些实体是
Person
Student
以及一些与此处无关的其他实体。这些实体是使用sql脚本初始化的,使用这些脚本初始化的实体使用Spring security登录可以正常工作

但是,我添加了保存新的
学生
并将其存储在H2数据库中的功能。当我将它们保存在数据库中,然后返回并尝试使用新创建的
Student
(和
Person
继承)登录时,Spring security拒绝验证。使用调试器,我发现它在内部抛出了一个
javax.persistence.NoResultFoundException
,消息是
找不到用于查询的实体。但我已经在几点上确认了新实体确实存在于数据库中。下面是我的Spring安全配置类和一些屏幕截图,显示了我在运行调试器时再现问题和一些结果

SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

private PersonService personService;

private CustomAuthentication customAuthentication;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(authenticationProvider());
}

@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/resources/**",
            "/static/**");
}

@Override
protected void configure(HttpSecurity http) throws Exception {

    http.authorizeRequests()
            .antMatchers("/*").hasAnyRole("STUDENT", "FACULTY_MEMBER", "STAFF_MEMBER")
            .and()
            .formLogin()
            .loginPage("/login")
            .loginProcessingUrl("/userAuth")
            .successHandler(customAuthentication)
            .permitAll()
            .and()
            .logout()
            .logoutUrl("/logout")
            .permitAll();

}

@Bean
public CsrfTokenRepository repo() {
    HttpSessionCsrfTokenRepository repo = new HttpSessionCsrfTokenRepository();
    repo.setParameterName("_csrf");
    repo.setHeaderName("X-CSRF-TOKEN");
    return repo;
}

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

@Bean
public DaoAuthenticationProvider authenticationProvider() {
    DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
    auth.setUserDetailsService(personService);
    auth.setPasswordEncoder(passwordEncoder());
    return auth;
}

@Autowired
public void setPersonService(PersonService personService) {
    this.personService = personService;
}

@Autowired
public void setCustomAuthentication(CustomAuthentication customAuthentication) {
    this.customAuthentication = customAuthentication;
}
}

My
UserDetailsService
实现。(
PersonRepo
扩展
UserDetailsService

这些截图试图演示该bug。在我点击注册按钮后,它会触发上面的
@PostMapping(“/save_student”)
,新的
student
对象被保存,然后重定向到显示数据库中所有
人员的页面。未列出新的
人员

这是日志的屏幕截图,显示新的“Steve Miller”
Student
可以在保存后立即检索。

通过
@GetMapping(/list\u students)
重定向后的结果。请注意,下面列出了“Steve Miller”

下面是尝试在“Steve Miller”下登录后的调试器。断点位于
UserDetailsService
实现中的
loadUserByUsername()
方法中

编辑:以下是指向异常堆栈跟踪和整个服务器日志的链接。


无论是日志记录还是它似乎存在的事实都不能说明什么。所有操作都在单个
EntityManager
中完成,结果将从一级缓存而不是数据库中检索。您的persons服务似乎不是事务性的(添加
@transactional
),因为它实际上能够将某些内容持久化到数据库中。感谢您的帮助!我没有意识到这可能是一个棘手的问题。Btw@Transactional在实际的存储库方法上进行了注释。如何解决此缓存问题,以便检索新的
Person
实例?控制器中的代码属于事务方法中的服务。也就是说,你最后的截图完全不可读,没有说明你应该说什么。还有哪个
loadByUsername
方法?您自己实现了它,所以我认为它称之为?您确定名称正确吗(不仅仅是名字?)。我将逻辑从控制器移动到了
PersonService
,并用
@Transactional
注释了
save()
,但错误仍在发生。
PersonService
中的
loadUserByUsername()
调用
this.loadByUsername
,调用
personRepo.loadByUsername()
personRepo
中的方法被注释为
@Transactional
。我不认为这是一个用户名问题,因为它们是以编程方式分配的,并且在列出数据库中的所有
人员时,会打印出正确的用户名。您只需将第一个字母小写(为什么不是全部?)。我不是在暗示生成的用户名,而是你用来登录的用户名。
@Service
public class PersonServiceImpl implements PersonService {

private PasswordEncoder passwordEncoder;

private PersonRepo personRepo;

@Override
public Person findByUsername(String username) {
    return personRepo.findByUsername(username);
}

@Override
public void save(Person person) {
    personRepo.save(person);
}

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    Logger logger = Logger.getLogger(getClass().toString());
    logger.info("Inside the loadUserByUsername method");
    Person person = this.findByUsername(username);
    if (person == null) {
        logger.info("Person with username: " + username + " was not found!");
        throw new UsernameNotFoundException("Could not find Person with username " + username);
    }
    logger.info("User was successfully retrieved from database");
    Collection<GrantedAuthority> grantedAuthorityRoles = person.getRoles().stream().map(
            role -> new SimpleGrantedAuthority(role.getRole().name())).collect(Collectors.toList());

    return new User(person.getUsername(), person.getPassword(), grantedAuthorityRoles);
}

@Autowired
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
    this.passwordEncoder = passwordEncoder;
}

@Autowired
public void setPersonRepo(PersonRepo personRepo) {
    this.personRepo = personRepo;
}
@PostMapping("/save_student")
public String saveStudent(@ModelAttribute("student") Student student) {
    student.setUsername(student.getFirstName().charAt(0) + student.getLastName().toLowerCase());
    student.setPassword(passwordEncoder.encode(student.getPassword()));
    student.setRoles(List.of(roleRepo.getRoleByName(RoleEnum.ROLE_STUDENT.name())));
    student.setVersion(1);
    studentRepo.save(student);
    if (personService.loadUserByUsername(student.getUsername()) == null) {
        throw new EntityNotFoundException();
    } else {
        logger.info("Person with username " + student.getUsername() + " was indeed found.");
    }
    return "redirect:list_students";
}

@GetMapping("/list_students")
public String listStudents(Model model) {
    model.addAttribute("students", studentRepo.findAll());
    model.addAttribute("people", personRepo.findAll());
    return "all_students";
}