• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

AjaxSecurityConfig 에러에 관해 질문드립니다.

22.10.06 22:21 작성 조회수 700

0

AjaxSecurityConfig 제작 중 authenticationManager must be specified 오류가 발생합니다.

@Configuration
@Order(0)
public class AjaxSecurityConfig {
    private AuthenticationConfiguration authenticationConfiguration;

    @Autowired
    private void setAjaxSecurityConfig(AuthenticationConfiguration authenticationConfiguration) {
        this.authenticationConfiguration = authenticationConfiguration;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public AccessDeniedHandler ajaxAccessDeniedHandler() {
        return new AjaxAccessDeniedHandler();
    }

    @Bean
    public AuthenticationProvider ajaxAuthenticationProvider(){
        return new AjaxAuthenticationProvider(passwordEncoder());
    }

    @Bean
    public AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler(){
        return new AjaxAuthenticationSuccessHandler();
    }

    @Bean
    public AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler(){
        return new AjaxAuthenticationFailureHandler();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain FilterChain(HttpSecurity http) throws Exception {
        AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
        authenticationManagerBuilder.authenticationProvider(ajaxAuthenticationProvider());

        http
                .antMatcher("/api/**")
                .authorizeRequests()
                .antMatchers("/api/messages").hasRole("MANAGER")
                .antMatchers("/api/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new AjaxLoginAuthenticationEntryPoint())
                .accessDeniedHandler(ajaxAccessDeniedHandler());
//        http.csrf().disable();

        ajaxConfigurer(http);

        return http.build();
    }

    private void ajaxConfigurer(HttpSecurity http) throws Exception {
        http
                .apply(new AjaxLoginConfigurer<>())
                .successHandlerAjax(ajaxAuthenticationSuccessHandler())
                .failureHandlerAjax(ajaxAuthenticationFailureHandler())
                .loginPage("/api/login")
                .loginProcessingUrl("/api/login")
                .setAuthenticationManager(authenticationManager(authenticationConfiguration));
    }

//    @Bean
//    public AjaxLoginProcessingFilter ajaxLoginProcessingFilter() throws Exception {
//        AjaxLoginProcessingFilter ajaxLoginProcessingFilter = new AjaxLoginProcessingFilter();
//        ajaxLoginProcessingFilter.setAuthenticationManager(authenticationManager(authenticationConfiguration));
//        ajaxLoginProcessingFilter.setAuthenticationSuccessHandler(ajaxAuthenticationSuccessHandler());
//        ajaxLoginProcessingFilter.setAuthenticationFailureHandler(ajaxAuthenticationFailureHandler());
//        return ajaxLoginProcessingFilter;
//    }
}

 

public final class AjaxLoginConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractAuthenticationFilterConfigurer<H, AjaxLoginConfigurer<H>, AjaxLoginProcessingFilter> {

    private AuthenticationSuccessHandler successHandler;
    private AuthenticationFailureHandler failureHandler;
    private AuthenticationManager authenticationManager;

    public AjaxLoginConfigurer() {
        super(new AjaxLoginProcessingFilter(), null);
    }

    @Override
    public void init(H http) throws Exception {
        super.init(http);
    }

    @Override
    public void configure(H http) {

        if(authenticationManager == null){
            authenticationManager = http.getSharedObject(AuthenticationManager.class);
        }
        getAuthenticationFilter().setAuthenticationManager(authenticationManager);
        getAuthenticationFilter().setAuthenticationSuccessHandler(successHandler);
        getAuthenticationFilter().setAuthenticationFailureHandler(failureHandler);

        SessionAuthenticationStrategy sessionAuthenticationStrategy = http
                .getSharedObject(SessionAuthenticationStrategy.class);
        if (sessionAuthenticationStrategy != null) {
            getAuthenticationFilter().setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
        }
        RememberMeServices rememberMeServices = http
                .getSharedObject(RememberMeServices.class);
        if (rememberMeServices != null) {
            getAuthenticationFilter().setRememberMeServices(rememberMeServices);
        }
        http.setSharedObject(AjaxLoginProcessingFilter.class,getAuthenticationFilter());
        http.addFilterBefore(getAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    public AjaxLoginConfigurer<H> loginPage(String loginPage) {
        return super.loginPage(loginPage);
    }

    public AjaxLoginConfigurer<H> successHandlerAjax(AuthenticationSuccessHandler successHandler) {
        this.successHandler = successHandler;
        return this;
    }

    public AjaxLoginConfigurer<H> failureHandlerAjax(AuthenticationFailureHandler authenticationFailureHandler) {
        this.failureHandler = authenticationFailureHandler;
        return this;
    }

    public AjaxLoginConfigurer<H> setAuthenticationManager(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        return this;
    }

    @Override
    protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
        return new AntPathRequestMatcher(loginProcessingUrl, "POST");
    }

}

 

Intellij 에서 AjaxLoginConfigurer에서 setAuthenticationManager 메소드 쪽에 Return value of the method is never used 라고 하는 것을 보면 Manager의 return 값이 없는 것 같습니다.

@Bean
    public AjaxLoginProcessingFilter ajaxLoginProcessingFilter() throws Exception {
    AjaxLoginProcessingFilter ajaxLoginProcessingFilter = new AjaxLoginProcessingFilter();
    ajaxLoginProcessingFilter.setAuthenticationManager(authenticationManager(authenticationConfiguration));
    ajaxLoginProcessingFilter.setAuthenticationSuccessHandler(ajaxAuthenticationSuccessHandler());
    ajaxLoginProcessingFilter.setAuthenticationFailureHandler(ajaxAuthenticationFailureHandler());
    return ajaxLoginProcessingFilter;
}

 

에서는 정상적으로 authenticationManager가 등록이 되었습니다. 오류가 왜 일어나는지는 알 것 같지만, 해결 방법이 도저히 이해가 가지 않습니다. 아니면 전에 올린 ajaxAuthenticationProvider가 등록되지 않은 것이 문제인지 생각했지만. Filter로는 잘 되어서 그 문제는 아닐 거라 생각해 질문을 올립니다.

전체 코드는 Othkkartho/SpringSecurityLearn at ch4.7 (github.com) 입니다. 좋은 강의와 질문 대답 항상 감사합니다.

답변 1

답변을 작성해보세요.

0

@Component
public class AjaxLoginProcessingFilter

 

위에서 @Component 를 제거해야 합니다

AjaxLoginProcessingFilter 를 빈으로 정의하게 되면 빈의 라이프 사이클에서

@Override
public void afterPropertiesSet() {
   Assert.notNull(this.authenticationManager, "authenticationManager must be specified");
}

위 메소드가 먼저 호출이 되기 때문에 실제

private void ajaxConfigurer(HttpSecurity http) throws Exception {
    http
            .apply(new AjaxLoginConfigurer<>())
            .successHandlerAjax(ajaxAuthenticationSuccessHandler())
            .failureHandlerAjax(ajaxAuthenticationFailureHandler())
            .loginPage("/api/login")
            .loginProcessingUrl("/api/login")
            .setAuthenticationManager(authenticationManager(authenticationConfiguration));
}

위이 메소드가 실행되지 못해서 AjaxLoginProcessingFilter 에서 필수값인 AuthenticationManager 를 저장하지 못하고 있습니다.

그리고 스프링 시큐리티에서 필터를 생성하고 등록할 때 대부분 빈으로 등록하지 않습니다.

public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
      AbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {

   /**
    * Creates a new instance
    * @see HttpSecurity#formLogin()
    */
   public FormLoginConfigurer() {
      super(new UsernamePasswordAuthenticationFilter(), null);
      usernameParameter("username");
      passwordParameter("password");
   }

 

초기화 설정에서 폼인증을 담당하는 필터인 UsernamePasswordAuthenticationFilter 를 생성하고 있는데 보시면 빈이 아닌 일반 POJO 로 생성하는 것을 알 수 있습니다. 그래서 빈의 라이프 사이클을 타지 않도록 합니다.

new UsernamePasswordAuthenticationFilter()

 

결론은 AjaxLoginProcessingFilter 의 @Component 를 삭제하면 됩니다.

네 답변 감사합니다.