userdetails tutorial example ejemplo authenticationmanagerbuilder spring spring-mvc spring-security servlet-3.0 spring-java-config

tutorial - Spring Security 3.2: @Autowire no funciona con la configuración de Java y AuthenticationProvider personalizado en la aplicación Spring MVC?



userdetailsservice spring security 4 (2)

Voy a suponer que UserService es una clase y tiene alguna anotación @Transactional en sí misma o en uno de sus métodos.

Deberá agregar CGLIB a su classpath y cambiar su @EnableTransactionManagement a

@EnableTransactionManagement(proxyTargetClass = true)

de modo que Spring utiliza el proxy CGLIB (que puede usar clases proxy) en lugar de los proxies JKD (que no pueden).

Alternativamente, puede crear una interfaz UserService e implementar (y anotar con @Service ) una clase UserServiceImpl . Su campo UserService UserService se mantendrá igual, pero Spring podrá usar proxies JDK.

Este problema se discute relativamente bien en varias publicaciones de blog y en preguntas de SO. Sin embargo, no pude encontrar uno que aborda específicamente el problema con la configuración de Java. Sospecho que estoy haciendo algo mal en mis archivos de configuración de Java, ya que he encontrado algunas publicaciones que indican que el problema se puede resolver eliminando la etiqueta XML de depuración ( https://jira.springsource.org/browse/ SEC-1885 ).

Estoy usando 3.2.0.RELEASE de seguridad de primavera, y 3.2.6.RELEASE de spring framework. Debajo de los archivos principales utilizados en la configuración spring security / mvc y el AuthenticationProvider personalizado.

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); } }

Spring está lanzando una excepción mientras construye su contexto de aplicación (durante la inicialización de la aplicación):

[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

No entiendo por qué está sucediendo, pero si UserDetailsService implementación de la interfaz UserService clase UserService , la aplicación se iniciará correctamente. Sin embargo, cuando ApplicationAuthenticationProvider es invocado por Spring, el UserService no se conecta automáticamente y la aplicación lanza una NullPointerException.

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


Descubrí cómo ponerlo a funcionar, aunque todavía quedan algunos problemas sin respuesta.

1) Todavía no sé por qué la inicialización del contexto Spring falla cuando UserService implementa UserDetailsService . Dado que no veo uso para él, ya que estoy usando un AuthenticationProvider personalizado, acabo de eliminar esta implementación y las cosas están bien por ahora. A mi leal saber y entender (por lo que pude entender desde mi primera lectura inicial de la documentación de referencia de Spring Security), el proporcionar un AuthenticationProvider personalizado o una implementación de UserDetailsService son alternativas exclusivas.

2) Tal como lo notó uno de los encuestados (@Sotirios Delimanolis) estaba instanciando ApplicatinoAuthenticationProvider a mano y como no estaba siendo administrado por Spring, esta instancia no tendría una instancia de UserService UserService automáticamente. En base a esto, cambié WebSecurityConfig para obtener una instancia de ApplicationAuthenticationProvider autocableado como se puede ver a continuación:

@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private ApplicationAuthenticationProvider authenticationProvider; @Override protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception { authManagerBuilder.authenticationProvider(authenticationProvider); } }

Esto aún no era suficiente, porque ApplicationAuthenticationProvider no se estaba autoconectando en WebSecurityConfig . Basado en este enlace Spring Security 3.1.3 @Autowired not Work cuando se usa WebApplicationInitializer Noté que esto se debía a que la configuración de seguridad también debería tener una declaración de exploración de componentes. Agregar @ComponentScan(basePackages = {"com.mypackage"}) a WebSecurityConfig resolvió el problema.