异步Firebase身份验证后,不允许过滤链继续

时间:2018-04-29 13:26:44

标签: spring google-app-engine spring-boot spring-security firebase-authentication

我正在尝试通过添加自定义身份验证过滤器和身份验证提供程序来使用firebase实现自定义身份验证。这两个似乎工作正常,但当过滤链应该继续时,抛出错误,请求永远不会到达控制器。我的应用程序使用spring boot并托管在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);
    }
}

在本地运行并将请求发送到应该进行身份验证的端点时,过滤器中callback-onsuccess中的try-catch会引发以下异常:

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 :(得分:0)

在尝试了一点之后我发现为了让过滤器继续,我必须等待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();
            }
        }
    }
}