Java Spring Security不会对新创建的用户进行身份验证,并抛出NoResultException,尽管用户已确认存在于数据库中
整个项目的代码可以在@ 我使用的是SpringSecurity5和SpringMVC项目,它由一个类an支持的自定义dao身份验证 实现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
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;
}
}
MyUserDetailsService
实现。(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";
}