inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

스프링 시큐리티

7) Ajax 로그인 구현 & CSRF 설정

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

해결된 질문

827

오스카르소

작성한 질문수 6

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) 입니다. 좋은 강의와 질문 대답 항상 감사합니다.

java Spring Security spring-boot

답변 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 를 삭제하면 됩니다.

0

오스카르소

네 답변 감사합니다.

시큐리티 공부 버전 질문

0

190

1

[해결 방법] MethodSecurityConfig.customMethodSecurityMetadataSource() 호출하지 않는 이슈

0

197

1

AbstractSecurityInterceptor.class.beforeInvocation()를 2번 실행하는 경우

0

186

1

강의 코드가 왜이렇게 뒤죽박죽인가요...

0

271

1

메인 페이지로 접속해도 login url로 리다이렉트가 되지 않습니다..

0

248

1

파라미터값이 넘어가지 않습니다 ....

0

382

1

security filterChain 설정 질문이 있습니다.

0

337

1

소스 부분 질문 드립니다.

0

213

2

섹션4 7번 강의 문제가 있는거 같네요.

0

352

2

파일이 수시로 이름이 바껴있네요 ㄷㄷ

0

312

1

HttpSessionSecurityContextRepository를 사용안하는 문제

0

566

2

error , exception 이 잘 안됩니다.

0

289

2

thymeleaf tag 질문합니다.

0

201

2

버전업하면서 deprecated된 것들이 너무많아요

0

483

1

spring security 패치 관련

0

442

1

모바일을 사용할때 토큰말고 세션

0

869

2

DB 연동한 인가 부분에 대한 질문입니다!

0

269

1

Ajax방식도 똑같이 Session방식을 사용하는건가요?

0

313

1

Config 파일 생성 시 질문이 있습니다.

0

238

1

강사님 몇일동안 구글 검색만 100개 했는데도 이유를 모르겠습니다..

1

443

2

403 에러 뜹니다.

0

828

2

login_proc의 존재에 대한 간략한 설명입니다

0

283

1

top.html에 로그인 링크를 만들어서 로그인을 해봤습니다

0

295

2

안녕하세요. DB에 저장될 때 이해 안 가는 값이 있어서 질문드립니다!

0

196

1