我刚开始通过阅读Spring Boot in Action这本书来学习 Spring Boot,我正在学习这本书的示例,尝试自己运行它们,但我有一个使用 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 接口(interface):

package com.lixin.readinglist; 
 
import org.springframework.data.jpa.repository.JpaRepository; 
 
/** 
 * @author lixin 
 */ 
public interface ReaderRepository extends JpaRepository<Reader, String> { 
} 

安全配置:

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); .
这意味着在您的情况下它接受 Example<Reader>并返回 Optional<Reader> .
你传递给它一个 String ,这是错误的,您将其用作 AuthenticationManagerBuilder.userDetailsService() 中的 lambda 返回值, 这也是错误的 因为UserDetailsService是定义为

的接口(interface)函数
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; 

所以你需要返回一个UserDetails实例不是 Optional它或扔掉UsernameNotFoundException如果与用户名不匹配 to be compliant with the javadoc :

Returns:

a fully populated user record (never null)

Throws:

UsernameNotFoundException - if the user could not be found or the user has no 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 session 还没有打开。
所以一个LazyInitializationException在任何情况下都会抛出(用户名正确或不正确):

org.hibernate.LazyInitializationException: could not initialize proxy - no Session 
        at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:155) 
        at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:268) 
        at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:73) 
        at davidhxxx.example.angularsboot.model.db.User_$$_jvstd90_5.isAccountNonLocked(User_$$_jvstd90_5.java) 
        at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider$DefaultPreAuthenticationChecks.check(AbstractUserDetailsAuthenticationProvider.java:352) 
        at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:165) 

This question你可能会感兴趣。


评论关闭
IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!