Фильтрующая цепь не может продолжаться после асинхронной аутентификации Firebase - PullRequest
0 голосов
/ 29 апреля 2018

Я пытаюсь реализовать пользовательскую аутентификацию с помощью firebase, добавив пользовательский фильтр аутентификации и провайдера аутентификации. Оба они, кажется, работают нормально, но когда цепочка фильтров должна продолжаться, выдается ошибка, и запрос никогда не достигает контроллера. Мое приложение использует весеннюю загрузку и размещено на Google App Engine.

AuthenticationFilter

public class FirebaseFilter extends OncePerRequestFilter {

    private static String HEADER_NAME = "X-Authorization-Firebase";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String xAuth = request.getHeader(HEADER_NAME);

        if (StringUtil.isEmptyOrWhitespace(xAuth)) {

            filterChain.doFilter(request, response);
            return;
        } else {
            ApiFutures.addCallback(FirebaseAuth.getInstance().verifyIdTokenAsync(xAuth),
                new ApiFutureCallback<FirebaseToken>() {

                    @Override
                    public void onFailure(Throwable throwable) {

                    }

                    @Override
                    public void onSuccess(FirebaseToken firebaseToken) {

                        FirebaseTokenHolder firebaseTokenHolder = new FirebaseTokenHolder(firebaseToken);

                        Authentication auth = new FirebaseAuthenticationToken(firebaseTokenHolder.getEmail(), firebaseTokenHolder);
                         SecurityContextHolder.getContext().setAuthentication(auth);

                        try {
                            filterChain.doFilter(request, response);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
        }
    }
}

AuthenticationProvider

@Component
public class FirebaseAuthenticationProvider implements AuthenticationProvider {

    public boolean supports(Class<?> authentication) {
        return (FirebaseAuthenticationToken.class.isAssignableFrom(authentication));
    }

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        if (!supports(authentication.getClass())) {
            return null;
        }

        FirebaseAuthenticationToken authenticationToken = (FirebaseAuthenticationToken) authentication;

        Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();
        grantedAuthorities.add(new SimpleGrantedAuthority("User"));

        UserDetails details = new org.springframework.security.core.userdetails.User(authenticationToken.getName(), "hello", grantedAuthorities);

        return new FirebaseAuthenticationToken(details, authentication.getCredentials(), details.getAuthorities());
    }

}

SpringBootExampleApplication

@SpringBootApplication
@EnableWebSecurity
@RestController
public class SpringBootExampleApplication extends WebSecurityConfigurerAdapter {

    @Autowired
    private FirebaseAuthenticationProvider authProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.authenticationProvider(authProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
         http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
               .and().addFilterBefore(new FirebaseFilter(), BasicAuthenticationFilter.class)
              .authorizeRequests()
                  .antMatchers("/", "/js/**") // Start page
                      .permitAll()
                  .anyRequest() // Everything that is not start-page is authenticated
                      .authenticated();//.
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringBootExampleApplication.class, args);
    }
}

При локальном запуске и отправке запроса в конечную точку, которая должна быть аутентифицирована, try-catch в callback-onsuccess в фильтре выдает следующее исключение:

java.lang.NullPointerException
[INFO] GCLOUD:  at com.google.appengine.tools.development.DevAppServerModulesFilter.getRequestType(DevAppServerModulesFilter.java:151)
[INFO] GCLOUD:  at com.google.appengine.tools.development.DevAppServerModulesFilter.doFilter(DevAppServerModulesFilter.java:113)
[INFO] GCLOUD:  at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1751)
[INFO] GCLOUD:  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
[INFO] GCLOUD:  at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
[INFO] GCLOUD:  at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
[INFO] GCLOUD:  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
[INFO] GCLOUD:  at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
[INFO] GCLOUD:  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
[INFO] GCLOUD:  at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
[INFO] GCLOUD:  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
[INFO] GCLOUD:  at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
[INFO] GCLOUD:  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
[INFO] GCLOUD:  at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
[INFO] GCLOUD:  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
[INFO] GCLOUD:  at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
[INFO] GCLOUD:  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
[INFO] GCLOUD:  at com.codaeasy.FirebaseFilter$1.onSuccess(FirebaseFilter.java:51)
[INFO] GCLOUD:  at com.codaeasy.FirebaseFilter$1.onSuccess(FirebaseFilter.java:33)
[INFO] GCLOUD:  at com.google.api.core.ApiFutures$1.onSuccess(ApiFutures.java:66)
[INFO] GCLOUD:  at com.google.common.util.concurrent.Futures$4.run(Futures.java:1132)
[INFO] GCLOUD:  at com.google.firebase.internal.TaskToApiFuture$1.onComplete(TaskToApiFuture.java:50)
[INFO] GCLOUD:  at com.google.firebase.tasks.OnCompleteCompletionListener$1.run(OnCompleteCompletionListener.java:54)
[INFO] GCLOUD:  at com.google.common.util.concurrent.MoreExecutors$DirectExecutor.execute(MoreExecutors.java:435)
[INFO] GCLOUD:  at com.google.firebase.tasks.OnCompleteCompletionListener.onComplete(OnCompleteCompletionListener.java:48)
[INFO] GCLOUD:  at com.google.firebase.tasks.TaskCompletionListenerQueue.flush(TaskCompletionListenerQueue.java:81)
[INFO] GCLOUD:  at com.google.firebase.tasks.TaskImpl.setResult(TaskImpl.java:95)
[INFO] GCLOUD:  at com.google.firebase.tasks.Tasks$1.run(Tasks.java:82)
[INFO] GCLOUD:  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
[INFO] GCLOUD:  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[INFO] GCLOUD:  at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
[INFO] GCLOUD:  at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
[INFO] GCLOUD:  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
[INFO] GCLOUD:  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
[INFO] GCLOUD:  at com.google.appengine.tools.development.BackgroundThreadFactory$1$1.run(BackgroundThreadFactory.java:60)

Я пытался исследовать эту ошибку онлайн, но ничего не нашел.

1 Ответ

0 голосов
/ 29 апреля 2018

Попробовав немного больше, я обнаружил, что для продолжения работы фильтра мне нужно подождать, пока Future не закончится в той же теме по какой-то причине. Вместо обратного вызова мне теперь нужно следующий код в моем фильтре

public class FirebaseFilter extends OncePerRequestFilter {

    private static String HEADER_NAME = "X-Authorization-Firebase";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String xAuth = request.getHeader(HEADER_NAME);

        if (StringUtil.isEmptyOrWhitespace(xAuth)) {

            filterChain.doFilter(request, response);
            return;
        } else {

            try {
                FirebaseToken token = FirebaseAuth.getInstance().verifyIdTokenAsync(xAuth).get();
                System.out.println("Result in filter: " + token.getEmail());

                Authentication auth = new FirebaseAuthenticationToken(token.getEmail(), token);
                SecurityContextHolder.getContext().setAuthentication(auth);

                filterChain.doFilter(request, response);

            } catch (Exception e) {
                System.out.println("Im here");
                e.printStackTrace();
            }
        }
    }
}
...