Spring Security 3.2:@Autowire 不适用于 Spring MVC 应用程序中的 java 配置和自定义 AuthenticationProvider? [英] Spring Security 3.2: @Autowire doesn't work with java configuration and custom AuthenticationProvider in Spring MVC application?

查看:38
本文介绍了Spring Security 3.2:@Autowire 不适用于 Spring MVC 应用程序中的 java 配置和自定义 AuthenticationProvider?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题在几篇博文和 SO 问题中得到了比较好的讨论.尽管如此,我还是找不到一个专门解决 java 配置问题的方法.我怀疑我在我的 java 配置文件中做错了什么,因为我发现一些帖子表明可以通过删除调试 XML 标记来解决问题(https://jira.springsource.org/browse/SEC-1885).

This problem is relatively well discussed in several blog posts and SO questions. Nevertheless, I wasn't able to find one specifically addressing the problem with java configuration. I'm suspecting that I'm doing something wrong in my java configuration files, since I've found some posts indicating that the problem can be resolved by removing the debug XML tag (https://jira.springsource.org/browse/SEC-1885).

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

I'm using 3.2.0.RELEASE of spring security, and 3.2.6.RELEASE of spring framework. Below the main files used in the spring security/mvc configuration and the custom AuthenticationProvider.

网络配置:

@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:

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

网络安全配置:

@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:

WebSecurityInitializer:

public class WebSecurityInitializer extends AbstractSecurityWebApplicationInitializer {

}

身份验证提供者:

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

用户服务:

@Service
public class UserService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;


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

Spring 在构建其应用程序上下文时(在应用程序初始化期间)抛出异常:

Spring is throwing an exception while it is building its application context (during application initialization):

[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

我不明白为什么会发生这种情况,但是如果我从 UserService 类中删除了 UserDetailsS​​ervice 接口实现,那么应用程序将成功启动.但是,当 ApplicationAuthenticationProvider 被 Spring 调用时,UserService 不会自动连接到其中,应用程序会抛出 NullPointerException.

I don't understand why it is happening, but if I remove the UserDetailsService interface implementation from UserService class, then the application starts successfully. However, when ApplicationAuthenticationProvider is invoked by Spring, the UserService is not autowired into it and the application throws a NullPointerException.

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

推荐答案

想出了如何让它发挥作用,但仍有一些问题没有解决.

Figured out how to put it to work, although there still some issues unanswered.

1) 当UserService 实现UserDetailsS​​ervice 时,我仍然不知道为什么Spring 上下文初始化失败.鉴于我没有看到它的用途,因为我使用的是自定义 AuthenticationProvider,我刚刚删除了这个实现,现在一切正常.据我所知(根据我第一次阅读 Spring Security 参考文档时的理解),提供自定义 AuthenticationProviderUserDetailsS​​ervice 实现是唯一的替代方案.

1) I still don't know why Spring context initialization fails when UserService implements UserDetailsService. Given that I'm not seeing use for it, since I'm using a custom AuthenticationProvider, I just removed this implementation and things are ok for now. To the best of my knowledge (from what I could understand from my first initial reading of Spring Security reference documentation) providing a custom AuthenticationProvider or an UserDetailsService implementation are exclusive alternatives.

2) 正如一位受访者 (@Sotirios Delimanolis) 所注意到的,我正在手动实例化 ApplicatinoAuthenticationProvider,并且由于它不是由 Spring 管理的,因此该实例不会有 UserService 实例自动连接到其中.基于此,我更改了 WebSecurityConfig 以获取 ApplicationAuthenticationProvider 的自动装配实例,如下所示:

2) As noticed by one of the respondents (@Sotirios Delimanolis) I was instantiating ApplicatinoAuthenticationProvider by hand and since it wasn't being managed by Spring this instance would not have an UserService instance autowired into it. Based on this, I changed WebSecurityConfig to get an autowired instance of ApplicationAuthenticationProvider as can be seen below:

@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 @使用 WebApplicationInitializer 时自动装配不工作 我注意到这是因为安全配置也应该有一个组件扫描声明.将 @ComponentScan(basePackages = {"com.mypackage"}) 添加到 WebSecurityConfig 解决了问题.

This wasn't still sufficient, because ApplicationAuthenticationProvider wasn't being autowired into WebSecurityConfig. Based on this link Spring Security 3.1.3 @Autowired not Work when using WebApplicationInitializer I noticed that this was because security config should have a component scan declaration too. Adding @ComponentScan(basePackages = {"com.mypackage"}) to WebSecurityConfig resolved the problem.

这篇关于Spring Security 3.2:@Autowire 不适用于 Spring MVC 应用程序中的 java 配置和自定义 AuthenticationProvider?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆