인프런 커뮤니티 질문&답변

오스카르소님의 프로필 이미지
오스카르소

작성한 질문수

스프링 시큐리티

3) 인증 처리자 - AjaxAuthenticationProvider

AjaxAuthenticationProvider가 ProviderManager에게 등록이 안됩니다.

작성

·

935

0

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

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

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

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

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

        http
                .antMatcher("/api/**")
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(ajaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class);

        http.csrf().disable();

        return http.build();
    }

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

AjaxSecurityConfig.java

 

AjaxAuthenticationProvider.java

@Component
public class AjaxAuthenticationProvider implements AuthenticationProvider {
    private UserDetailsService userDetailsService;
    private PasswordEncoder passwordEncoder;

    @Autowired
    private void setAjaxAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    @Transactional
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        AccountContext accountContext = FormAuthenticationProvider.authenticationIf(authentication, userDetailsService, passwordEncoder);

        return new AjaxAuthenticationToken(accountContext.getAccount(), null, accountContext.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(AjaxAuthenticationToken.class);
    }
}

 

설정을 모두 완료하고 확인해보니 Filter는 정상적으로 등록이 되고, ProviderManager에 넘기는 것 까지는 진행이 됩니다. 그런대 ProviderManager에 BreakPoint를 걸고 확인해 보니 DaoAuthenticationProvider만 providers에 등록이 되어있습니다. 그래서 Form 방식을 확인해 보니 Form은 정상작동 하는 것을 확인했습니다. 혹시 자세한 코드가 필요하시다면 아래 링크의 브런치 ch4.3입니다. 항상 모든 질문에 최선을 다해 답변해 주시니 감사합니다.

추가 사항으로 SecurityConfig에 넣어서 돌렸더니 Provider가 정상적으로 추가되는 것을 확인했습니다. 또한 오류는 인식하고, AjaxAuthenticationFailureHandler는 또 호출을 합니다. 점점 뭐가 문제인지 잘 모르겠습니다.

Othkkartho/SpringSecurityLearn at ch4.3 (github.com)

답변 2

2

정수원님의 프로필 이미지
정수원
지식공유자

일단 코드는 다음과 같이 수정되어야 합니다.

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
    ProviderManager authenticationManager = (ProviderManager)authenticationConfiguration.getAuthenticationManager();
    authenticationManager.getProviders().add(ajaxAuthenticationProvider());
    return authenticationManager;
}

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

 

사실 이 부분은 스프링 시큐리티의 내부 구조를 정확하게 이해하지 못하면 혼돈하게 되는데요..

AuthenticationManager 는 초기화 때 생성되어 기본적으로 DaoAuthenticationProvider 와 같은 객체를 가지고 있습니다.

그리고 UsernamePasswordAuthenticationFilter 와 같은 클래스에서 참조하고 있습니다.

그렇다면 ajaxAuthenticationProvider 도 초기화때 생성된 AuthenticationManager 에서 추가해 주어야 합니다.

바로

AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);

authenticationManagerBuilder.authenticationProvider(ajaxAuthenticationProvider());

위 코드가 초기화 때 생성된 AuthenticationManager 입니다.

그런데 실제 AjaxLoginProcessingFilter 를 생성하는 코드를 보시면

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

초기화 때 생성된 동일한 AuthenticationManager 가 아닌 다른 AuthenticationManager 를 참조하고 있습니다.

즉 authenticationConfiguration.getAuthenticationManager(); 에서 참조하고 있는 AuthenticationManager 와 authenticationManagerBuilder.authenticationProvider(ajaxAuthenticationProvider()); 를 통해 참조되는 AuthenticationManager 는 동일한 객체가 아닙니다.

그렇기 때문에 AjaxLoginProcessingFilter 에서 참조하고 있는 AuthenticationManager 에 ajaxAuthenticationProvider 를 추가해 주어야 정상동작하게 됩니다.

위 코드로 수정한 후에 디버깅해 보시면 AuthenticationManager 가 서로 다른 두개의 객체가 생성되어 있음을 알게 됩니다.

조금 어려운 개념이긴 하지만 ProviderManager 의 생성과정을 이해하시는게 중요합니다

아 감사합니다. 공부 하면 할수록 공부할께 더 많아지는 마법이 있는거 같네요.

0

강의내용과 현재 최신버전이 너무많이바뀌어서 하나하나 찾아가면서 공부중인데 시간은 오래걸려도 공부는잘되네요 ㅋㅋ

오스카르소님의 프로필 이미지
오스카르소

작성한 질문수

질문하기