복붙노트

[SPRING] Spring Security 3.2 : @Autowire는 Spring MVC 애플리케이션에서 자바 설정과 커스텀 AuthenticationProvider와 작동하지 않습니까?

SPRING

Spring Security 3.2 : @Autowire는 Spring MVC 애플리케이션에서 자바 설정과 커스텀 AuthenticationProvider와 작동하지 않습니까?

이 문제는 여러 블로그 게시물 및 SO 질문에서 비교적 잘 논의됩니다. 그럼에도 불구하고, 나는 자바 구성에 대한 문제를 특별히 해결하는 것을 찾을 수 없었다. 디버그 XML 태그 (https://jira.springsource.org/browse/)를 제거하여 문제가 해결 될 수 있음을 알리는 게시물을 발견했기 때문에 Java 구성 파일에서 잘못된 작업을하고있는 것으로 의심됩니다. SEC-1885).

스프링 보안 3.2.0.RELEASE 및 스프링 프레임 워크 3.2.6.RELEASE를 사용하고 있습니다. 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());
    }
}

웹 보안 초기화 프로그램 :

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은 애플리케이션 초기화 동안 애플리케이션 컨텍스트를 구축하는 동안 예외를 던지고있다.

[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 클래스에서 UserDetailsService 인터페이스 구현을 제거하면 응용 프로그램이 성공적으로 시작됩니다. 그러나 ApplicationAuthenticationProvider가 Spring에 의해 호출 될 때 UserService는 자동 실행되지 않으며 응용 프로그램은 NullPointerException을 발생시킵니다.

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

해결법

  1. ==============================

    1.비록 문제가 아직 풀리지 않았지만 그것을 작동시키는 법을 알아 냈습니다.

    비록 문제가 아직 풀리지 않았지만 그것을 작동시키는 법을 알아 냈습니다.

    1) UserService가 UserDetailsService를 구현할 때 Spring 컨텍스트 초기화가 실패하는 이유는 아직도 모르겠다. 내가 사용자 정의 AuthenticationProvider를 사용하고 있기 때문에 그것을 사용하지 않는다는 것을 감안할 때, 나는 지금이 구현을 제거하고 지금은 괜찮습니다. 커스텀 AuthenticationProvider 나 UserDetailsService 구현을 제공하는 내 지식 (Spring Security 참조 문서를 처음 읽었을 때 이해할 수있는 것부터)은 독점적 인 대안입니다.

    2) 응답자 (@Sotirios Delimanolis) 중 한 사람이 주목 한 것처럼 ApplicatinoAuthenticationProvider를 수동으로 인스턴스화했으며 Spring에 의해 관리되지 않았으므로이 인스턴스에 Autowired UserService 인스턴스가 없을 것입니다. 이를 바탕으로 WebSecurityConfig를 변경하여 ApplicationAuthenticationProvider의 자동 인스턴스를 아래와 같이 얻을 수 있습니다.

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

    ApplicationAuthenticationProvider가 WebSecurityConfig로 autowired되지 않았기 때문에 이것은 여전히 ​​충분하지 않았습니다. 이 링크를 기반으로 Spring Security 3.1.3 @Autowired WebApplicationInitializer를 사용할 때 작동하지 않음 보안 설정에 구성 요소 검사 선언이 있어야하기 때문에 WebApplicationInitializer가 사용 된 것으로 나타났습니다. WebSecurityConfig에 @ComponentScan (basePackages = { "com.mypackage"})을 추가하면 문제가 해결되었습니다.

  2. ==============================

    2.UserService가 클래스이고 그 자체 또는 메소드 중 하나에 @Transactional 주석이 있다고 가정합니다.

    UserService가 클래스이고 그 자체 또는 메소드 중 하나에 @Transactional 주석이 있다고 가정합니다.

    클래스 경로에 CGLIB를 추가하고 @EnableTransactionManagement를 다음과 같이 변경해야합니다.

    @EnableTransactionManagement(proxyTargetClass = true)
    

    Spring은 JKD 프록시 대신에 CGLIB 프록시 (클래스를 프록시 처리 할 수있다)를 사용한다.

    또는 UserService 인터페이스를 만들고 UserServiceImpl 클래스를 구현하고 @Service로 주석을 추가 할 수 있습니다. autowired UserService 필드는 동일하게 유지되지만 Spring은 JDK 프록시를 사용할 수 있습니다.

  3. from https://stackoverflow.com/questions/20753979/spring-security-3-2-autowire-doesnt-work-with-java-configuration-and-custom-a by cc-by-sa and MIT license