簡體   English   中英

Spring 錯誤“名為 'x' 的 Bean 應為 'y' 類型,但實際上是 [com.sun.proxy.$Proxy] 類型”

[英]Spring error “Bean named 'x' is expected to be of type 'y', but was actually of type [com.sun.proxy.$Proxy]”

我正在嘗試使用 Spring Security 在應用程序中實現基於 DAO 的身份驗證。

當我嘗試使用用戶登錄應用程序時,出現此錯誤:

failed to lazily initialize a collection of role: com.intellivest.app.dao.User.groups, could not initialize proxy - no Session

看着@jcmwright80 對這個問題的回答,我明白我最好將UserDetailsServiceImpl erviceImpl class 注釋為@Transactional 這樣做之后,我在登錄時遇到錯誤:

Bean named 'userDetailsService' is expected to be of type 'com.intellivest.app.service.UserDetailsServiceImpl' but was actually of type 'com.sun.proxy.$Proxy238'"}}

這似乎是與在 UserDetailsServiceImpl 上創建的代理 object 相關的問題 - 我該如何優雅地解決這個問題?

代碼

安全配置的相關部分:

@Configuration
@ComponentScan("com.example.app.service")
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter  {

    @Bean
    public UserDetailsService userDetailsService() {
        return new UserDetailsServiceImpl();
    }
    
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

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

}

UserDetailsServiceImpl.java

@Service
@Transactional
public class UserDetailsServiceImpl implements UserDetailsService{

    public UserDetailsServiceImpl () {};
    
    @Autowired
    private UserDao userDao;
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        
        User user = userDao.getUser(username);
        
        if (user == null) {
            throw new UsernameNotFoundException ("User not found.");
        }
        return new UserDetailsImpl(user);
    }
  }

用戶.java

@Entity
@Table(name="users",schema="sec")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="userGen")
    @SequenceGenerator(name="userGen", sequenceName="user_id_seq", schema="sec")
    private long id;    

    // Validation constraints on the fields ...
    private String username;
    private String password;
    private boolean enabled;
    
    @ManyToMany
    @JoinTable(name="group_members", schema="sec", joinColumns= { @JoinColumn(name="user_id") }, inverseJoinColumns = { @JoinColumn(name="group_id") } )
    private Set<Group> groups;

 // Getters, Setters etc. ...
 }

(在UserGroup類的集合類型字段上使用@ManyToMany(fetch = FetchType.EAGER)的替代解決方案有效,盡管它可能會影響性能。)

解決方案 1(最佳)

@Service class 中使用的方法都應該在@Service實現的接口中聲明。 然后可以在任何地方通過其接口類型引用該服務(例如在@Controller類中)。 這樣 Spring 可以繼續使用 JDK 動態代理(優於 CGLIB)。

這一點尤其重要,因為實現了 Spring 提供的接口: org.springframework.security.core.userdetails.UserDetailsService ervice 只聲明了一種方法,但也需要其他方法。
這與其說是一個簡單的編碼問題,不如說是一個架構問題。

一步步:

public interface CustomizedUserDetailsService extends UserDetailsService {
    void add(User user);
}

實現服務中的方法:

@Service
public class UserDetailsServiceImpl implements CustomizedUserDetailsService{
    @Override
    @Transactional
    public void add (User user) {
        //...
    }
}

通過@Configuration中的接口類型引用 bean,但返回實現

@Bean
@Primary
public CustomizedUserDetailsService userDetailsService() {
    return new UserDetailsServiceImpl();
}

在使用實現時,通過對其實現接口的引用來注入 bean:

@Controller
public class UserController {

    private CustomizedUserDetailsService userDetailsService;
    
    @Autowired
    public void setUsersService(CustomizedUserDetailsService customizedUserDetailsService) {
        this.userDetailsService = customizedUserDetailsService;
    }
}

更多關於代理的細節在這里

解決方案 2

使用注解在服務 class 上啟用 CGLIB 代理。 這需要 spring-aop 和 aspectjweaver 依賴項(不使用 Spring Boot)。

@EnableAspectJAutoProxy(proxyTargetClass=true)

然后安全配置 class 可以像這樣生成 bean:

@Bean
public UserDetailsServiceImpl userDetailsService() {
    return new UserDetailsServiceImpl();
}

(DAO 層不應該是@Transactional ,應該只應用於@Service class 或后者 class 的特定方法。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM