我刚刚通过阅读《 Spring Boot in Action》 一书开始学习Spring Boot,并且正在 学习 本书 的示例 ,尝试自己运行它们,但是使用时遇到了问题JpaRepository.findOne()。
JpaRepository.findOne()
我遍及本章以查找可能的不匹配之处。但是,它根本不起作用。
该项目应该是一个简单的阅读清单。
这是代码:
读者@Entity:
package com.lixin.readinglist; import org.springframework.data.annotation.Id; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import javax.persistence.Entity; import java.util.Collection; import java.util.Collections; /** * @author lixin */ @Entity public class Reader implements UserDetails { private static final long serialVersionUID = 1L; @Id private String username; private String fullname; private String password; @Override public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getFullname() { return fullname; } public void setFullname(String fullname) { this.fullname = fullname; } @Override public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return Collections.singletonList(new SimpleGrantedAuthority("READER")); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
Jpa界面:
package com.lixin.readinglist; import org.springframework.data.jpa.repository.JpaRepository; /** * @author lixin */ public interface ReaderRepository extends JpaRepository<Reader, String> { }
SecurityConfig:
package com.lixin.readinglist; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; 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.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; /** * @author lixin */ @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private final ReaderRepository readerRepository; @Autowired public SecurityConfig(ReaderRepository readerRepository) { this.readerRepository = readerRepository; } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/").access("hasRole('READER')") .antMatchers("/**").permitAll() .and() .formLogin() .loginPage("/login") .failureUrl("/login?error=true"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService((UserDetailsService) username -> readerRepository.findOne(username)); } }
而且我一直收到这个错误:
Error:(40, 86) java: method findOne in interface org.springframework.data.repository.query.QueryByExampleExecutor<T> cannot be applied to given types; required: org.springframework.data.domain.Example<S> found: java.lang.String reason: cannot infer type-variable(s) S (argument mismatch; java.lang.String cannot be converted to org.springframework.data.domain.Example<S>)
findOne()定义为<S extends T> Optional<S> findOne(Example<S> example);。 这意味着在您的情况下,它接受a Example<Reader>并返回Optional<Reader>。 您将其传递给a String,这是错误的,并且将其用作lambda return in AuthenticationManagerBuilder.userDetailsService(),这也是错误的,因为UserDetailsService接口函数定义为
findOne()
<S extends T> Optional<S> findOne(Example<S> example);
Example<Reader>
Optional<Reader>
String
AuthenticationManagerBuilder.userDetailsService()
UserDetailsService
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
因此,您需要返回一个UserDetails实例,而不是实例Optional;UsernameNotFoundException如果与用户名不匹配,则抛出该实例以与javadoc兼容:
UserDetails
Optional
UsernameNotFoundException
返回值: 完全填充的用户记录(绝不为null) 抛出: UsernameNotFoundException-如果找不到用户或用户没有GrantedAuthority
返回值:
完全填充的用户记录(绝不为null)
抛出:
UsernameNotFoundException-如果找不到用户或用户没有GrantedAuthority
此外,您无需使用findOne()该示例查询。通过ID查询就足够了。
所以你可以这样写:
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(username -> readerRepository.findById(username) .orElseThrow( () -> new UsernameNotFoundException("user with username " + username + " not found")); }
附带说明一下,它getOne()很棘手,因为它依赖于延迟加载,在某些情况下可能会带来令人惊讶的意外。 JB Nizet的话很有趣。因此,我现在进行了测试。当isAccountNonLocked()Spring Security类访问实体(即)时,JPA会话仍然没有打开。 因此LazyInitializationException,无论如何都抛出a (用户名正确或否):
getOne()
isAccountNonLocked()
LazyInitializationException
org.hibernate.LazyInitializationException:无法初始化代理-没有会话 在org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:155) 在org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:268) 在org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:73) 在davidhxxx.example.angularsboot.model.db.User _ $$ _ jvstd90_5.isAccountNonLocked(User _ $$ _ jvstd90_5.java) 在org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider $ DefaultPreAuthenticationChecks.check(AbstractUserDetailsAuthenticationProvider.java:352) 在org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:165)