6

這個問題在幾篇博客文章和SO問題中討論得相當好。不過,我無法找到專門解決java配置問題的人。我懷疑我在我的java配置文件中做錯了什麼,因爲我發現一些帖子指出可以通過刪除調試XML標記(https://jira.springsource.org/browse/SEC-1885)來解決問題。Spring Security 3.2:@Autowire不能在Spring MVC應用程序中使用java配置和自定義AuthenticationProvider?

我使用的是3.2.0.RELEASE的spring security和3.2.6.RELEASE的spring框架。在spring security/mvc配置和自定義AuthenticationProvider中使用的主要文件下面。

WebConfig:

@Configuration 
@EnableWebMvc 
@ComponentScan(basePackages = {"com.mypackage"}) 
@ImportResource({ "classpath:/spring-data.xml", "classpath:/trace-context.xml" }) 
@EnableTransactionManagement 
public class WebConfig extends WebMvcConfigurerAdapter { 

    @Override 
    public void addViewControllers(ViewControllerRegistry registry) { 
     registry.addViewController("/login").setViewName("login"); 
    } 

    @Bean 
    public StandardServletMultipartResolver multipartResolver() { 
     return new StandardServletMultipartResolver(); 
    } 

    @Bean(destroyMethod = "shutdown") 
    public GraphDatabaseService graphDatabaseService() { 
     return new GraphDatabaseFactory().newEmbeddedDatabase("target/temp.db"); 
    } 

    @Bean 
    public RepositoryInitializer repositoryInitializer() { 
     return new RepositoryInitializer(); 
    } 

    @Override 
    public void addResourceHandlers(ResourceHandlerRegistry registry) { 
     registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); 
    } 

    @Override 
    public void addInterceptors(InterceptorRegistry registry) { 
     LocaleChangeInterceptor localeChangeInterceptor = new   LocaleChangeInterceptor(); 
     localeChangeInterceptor.setParamName("lang"); 
     registry.addInterceptor(localeChangeInterceptor); 
    } 

    @Bean 
    public LocaleResolver localeResolver() { 
     CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver(); 
     cookieLocaleResolver.setDefaultLocale(StringUtils.parseLocaleString("en")); 
     return cookieLocaleResolver; 
    } 

    @Bean 
    public ViewResolver viewResolver() { 
     InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); 
     viewResolver.setViewClass(JstlView.class); 
     viewResolver.setPrefix("/WEB-INF/views/"); 
     viewResolver.setSuffix(".jsp"); 
     return viewResolver; 
    } 

    @Bean 
    public MessageSource messageSource() { 
     ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 
     messageSource.setBasenames("classpath:messages/messages", "classpath:messages/validation"); 
     // if true, the key of the message will be displayed if the key is not 
     // found, instead of throwing a NoSuchMessageException 
     messageSource.setUseCodeAsDefaultMessage(true); 
     messageSource.setDefaultEncoding("UTF-8"); 
     // # -1 : never reload, 0 always reload 
     messageSource.setCacheSeconds(0); 
     return messageSource; 
    } 
} 

WebInitializer:

public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { 

    @Override 
    protected Class<?>[] getRootConfigClasses() { 
     return new Class[] { WebSecurityConfig.class }; 
    } 

    @Override 
    protected Class<?>[] getServletConfigClasses() { 
     return new Class<?>[] { WebConfig.class}; 
    } 

    @Override 
    protected String[] getServletMappings() { 
     return new String[] { "/" }; 
    } 

    @Override 
    protected Filter[] getServletFilters() { 
     CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); 
     characterEncodingFilter.setEncoding("UTF-8"); 
     return new Filter[] { characterEncodingFilter, new SiteMeshFilter()}; 
    } 

    @Override 
    public void onStartup(ServletContext servletContext) throws ServletException { 
     super.onStartup(servletContext); 
     //servletContext.addListener(new HttpSessionEventPublisher()); 
    } 
} 

WebSecurityConfig:

@Configuration 
@EnableWebSecurity 
@Order(1) 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http 
      .authorizeRequests().anyRequest().permitAll(); 
     // .antMatchers("/", "/login").permitAll() 
     // .anyRequest().authenticated(); 
     http 
      .formLogin() 
       .defaultSuccessUrl("/hello") 
       .loginPage("/login") 
       .permitAll() 
       .and() 
      .logout() 
       .logoutUrl("/logout") 
       .permitAll(); 
     http  
      .sessionManagement() 
      .maximumSessions(1) 
      .maxSessionsPreventsLogin(true); 

    }  

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

    @Override 
    protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception { 
     authManagerBuilder.authenticationProvider(new ApplicationAuthenticationProvider()); 
    } 
} 

WebSecurityInitializer:

public class WebSecurityInitializer extends AbstractSecurityWebApplicationInitializer { 

} 

的AuthenticationProvider:

@Component(value = "authenticationProvider") 
public class ApplicationAuthenticationProvider implements AuthenticationProvider { 

    @Autowired 
    public UserService userService; 

    public ApplicationAuthenticationProvider() {} 

    @Override 
    public Authentication authenticate(Authentication authentication) throws AuthenticationException { 
     String username = authentication.getName(); 
     String password = (String) authentication.getCredentials(); 

     User user = userService.loadUserByUsername(username); 

     if (user == null) { 
      throw new BadCredentialsException("Username not found."); 
     } 

     if (!password.equals(user.getPassword())) { 
      throw new BadCredentialsException("Wrong password."); 
     } 

     Collection<? extends GrantedAuthority> authorities = user.getAuthorities(); 

     return new UsernamePasswordAuthenticationToken(username, password, authorities); 
    } 

    @Override 
    public boolean supports(Class<?> arg0) { 
     return true; 
    } 
} 

UserService:雖然它正在建設它的應用程序上下文(應用程序初始化期間)

@Service 
public class UserService implements UserDetailsService { 

    @Autowired 
    private UserRepository userRepository; 


    @Override 
    public User loadUserByUsername(String username) throws UsernameNotFoundException { 
     return userRepository.findByUsername(username); 
    } 
} 

春天拋出異常:

[ERROR] [main 11:53:37] (FrameworkServlet.java:initServletBean:467) Context  initialization failed 
org.springframework.beans.factory.BeanCreationException: Error creating bean with name   'authenticationProvider': Injection of autowired dependencies failed; nested exception is  org.springframework.beans.factory.BeanCreationException: Could not autowire field: public  com.evidencefactory.service.UserService  com.evidencefactory.security.ApplicationAuthenticationProvider.userService; nested  exception is java.lang.IllegalArgumentException: Can not set  com.evidencefactory.service.UserService field  com.evidencefactory.security.ApplicationAuthenticationProvider.userService to  sun.proxy.$Proxy71 

我不明白爲什麼會發生,但如果我從刪除UserDetailsService接口實施210類,那麼應用程序將成功啓動。但是,當Spring調用ApplicationAuthenticationProvider時,UserService未被自動裝入,應用程序拋出NullPointerException。

java.lang.NullPointerException 
at com.evidencefactory.security.ApplicationAuthenticationProvider.authenticate(ApplicationAuthenticationProvider.java:33) 
+0

讓我們來看看你的UserService類。 –

+0

@SotiriosDelimanolis編輯。 – pasemes

回答

4

想通了如何使其工作,雖然還有一些問題沒有答案。

1)我仍然不知道爲什麼Spring上下文初始化失敗,當UserService實現UserDetailsService。鑑於我沒有看到它的使用,因爲我使用的是自定義的AuthenticationProvider,我只是刪除了這個實現,現在的事情都可以。 據我所知(從我第一次閱讀Spring Security參考文檔時可以理解的),提供定製的AuthenticationProviderUserDetailsService實現是唯一的替代方案。

2)注意到了的,我實例用手ApplicatinoAuthenticationProvider受訪者(@Sotirios Delimanolis)之一,因爲它不是由Spring管理這種情況下不會有一個UserService例如自動裝配進去。在此基礎上,我改變WebSecurityConfig得到的ApplicationAuthenticationProvider的自動裝配Autowired實例作爲可以看到下面:

@Configuration 
@EnableWebSecurity 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Autowired 
    private ApplicationAuthenticationProvider authenticationProvider; 

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

這仍然不足夠,因爲ApplicationAuthenticationProvider沒有被自動裝配成WebSecurityConfig。基於這個鏈接Spring Security 3.1.3 @Autowired not Work when using WebApplicationInitializer我注意到這是因爲安全配置也應該有一個組件掃描聲明。加入@ComponentScan(basePackages = {"com.mypackage"})WebSecurityConfig解決了這個問題。

3

我要去假設UserService是一類,並有一些@Transactional註釋無論是在本身或其中的一個方法。

你需要CGLIB添加到您的類路徑,改變你的@EnableTransactionManagement

@EnableTransactionManagement(proxyTargetClass = true) 

使Spring使用CGLIB代理(可代理類),而不是JKD代理(不能)。


或者,你可以創建一個接口UserService和實施(與@Service註釋)一UserServiceImpl類。您的自動編寫的UserService字段將保持不變,但Spring將能夠使用JDK代理。

+0

將@EnableTransactionManagement(proxyTargetClass = true)註釋添加到UserService類。錯誤已更改爲org.springframework.beans.factory.BeanCreationException:創建名爲'evidenceEditorController'的bean時出錯:注入自動裝配的依賴項失敗;嵌套異常是org.springframework.beans.factory.BeanCreationException:無法自動裝入字段:private com.evidencefactory.service.TermService com.evidencefactory.controller.EvidenceEditorController.termService;嵌套的異常是org.springframework.beans.factory.BeanCreationException:... – pasemes

+0

@pasemes註釋在'WebConfig'上,而不是在'UserService'上。 –

+0

如果我不使用Spring Security java配置,即沒有WebSecurityConfig和WebSecurityInitializer配置類,則不會發生此錯誤。我不理解這種行爲。 – pasemes

相關問題