首页 > 解决方案 > 尝试使用 AbstractPreAuthenticatedProcessingFilter 实现访问特定 URL 后出现 IllegalStateException

问题描述

我们扩展了 AbstractPreAuthenticatedProcessingFilter 来验证 shibboleth 用户,它没有什么特别的,只是从特定的请求属性中获取用户名并返回一个 PreAuthenticatedAuthenticationToken。

如果我们访问应用程序的根上下文,它可以正常工作,但如果我们在应用程序中请求特定的 URL,我们会得到“java.lang.IllegalStateException:响应已提交”。

在 SecurityConfig 我们有:

@Override
    protected void configure(final HttpSecurity http) throws SecurityException {
        try {

        http.authorizeRequests().antMatchers("/favicon.ico", "/resources/**", "/signin**").permitAll()
            .anyRequest().authenticated().and().formLogin().loginPage("/signin").permitAll()
            .failureUrl("/signin?error=1").successHandler(new CustomAuthenticationSuccessHandler())
            .loginProcessingUrl("/authenticate").and().sessionManagement().invalidSessionUrl("/signin")
            .and().exceptionHandling().accessDeniedHandler(new AccessDeniedHandler()).and().headers()
            .frameOptions().disable();

        
            http.addFilterBefore(
                new ShibbolethPreAuthenticatedProcessingFilter(new ShibbolethAuthenticationManager(),
                    new CustomAuthenticationSuccessHandler()),
                AbstractPreAuthenticatedProcessingFilter.class);
            http.logout().logoutUrl("/logout")
                .logoutSuccessUrl(environment.getProperty(Constantes.CT_SHIBBOLETH_LOGOUT_URL))
                .clearAuthentication(true).invalidateHttpSession(true);
        

        } catch (final Exception e) {
        throw new SecurityException(e);
        }
    }

ShibbolethAuthenticationManager 实现 AuthenticationManager 并且只是将 UserDetails 和 GrantedAuthorities 加载到 PreAuthenticatedAuthenticationToken

CustomAuthenticationSuccessHandler 扩展 SavedRequestAwareAuthenticationSuccessHandler 并将用户语言加载到 LocaleProvider 并将当前许可证详细信息加载到会话中。

如果我们在应用程序中请求“/”,它需要我们:

 @ResponseStatus(HttpStatus.MOVED_PERMANENTLY)
        @RequestMapping("/")
        public String index(HttpServletRequest httpRequest, HttpSession session, Authentication auth) {
        if (auth.getAuthorities().contains(new SimpleGrantedAuthority(Constants.CONTROL_PANEL_PERMISSION))) {
           return "redirect:/controlpanel/dashboard";
        }  else if (auth.getAuthorities().contains(new SimpleGrantedAuthority(Constants.CONFIG_PERMISSION))) 
        {
           return "redirect:/config";
        } else {
           return "redirect:/userpage";
        }
    }

我不知道为什么这些重定向有效,但是当我们请求另一个 URL 时,例如,如果我们请求直接转到 /controlpanel/dashboard,我们会收到错误消息。

@RequestMapping("/controlpanel/dashboard")    
public String dashboard(Model model, HttpServletRequest httpRequest, HttpSession session,
            @RequestParam(name = "comite", defaultValue = "-1") Long comite,
            @RequestParam(required = false) final Boolean tasksToDo) {
    
        // This method does nothing but add things to the  model object, such as:
        model.addAttribute("tasks", tasksToDo);
        // and then it takes the user to the view page
    
        return "cuadromando/dashboard";
        }

编辑:堆栈跟踪:

2020-12-02 14:14:07|ERROR|NA|01.10.00|http-nio-8080-exec-2|java.lang.IllegalStateException: No puedo reenviar después de que la respuesta se haya llevado a cabo.
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:323)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312)
    at org.sitemesh.webapp.WebAppContext.dispatch(WebAppContext.java:182)
    at org.sitemesh.webapp.WebAppContext.decorate(WebAppContext.java:157)
    at org.sitemesh.BaseSiteMeshContext.decorate(BaseSiteMeshContext.java:39)
    at org.sitemesh.webapp.SiteMeshFilter.postProcess(SiteMeshFilter.java:83)
    at org.sitemesh.webapp.contentfilter.ContentBufferingFilter.bufferAndPostProcess(ContentBufferingFilter.java:175)
    at org.sitemesh.webapp.contentfilter.ContentBufferingFilter.doFilter(ContentBufferingFilter.java:126)
    at org.sitemesh.webapp.SiteMeshFilter.doFilter(SiteMeshFilter.java:120)
    at org.sitemesh.config.ConfigurableSiteMeshFilter.doFilter(ConfigurableSiteMeshFilter.java:163)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doFilter(AbstractPreAuthenticatedProcessingFilter.java:121)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:347)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:263)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at es.bahiasoftware.meet.web.filter.RefererManagerFilter.doFilter(RefererManagerFilter.java:49)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.multipart.support.MultipartFilter.doFilterInternal(MultipartFilter.java:122)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:178)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:668)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:770)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

标签: javaspringspring-securitypre-authentication

解决方案


最后我通过实现 AuthenticationSuccessHandler 而不是扩展 SavedRequestAwareAuthenticationSuccessHandler 来解决它:

CustomSuccessHandler implements AuthenticationSuccessHandler

所以我摆脱了提交的响应,因为 SavedRequestAwareAuthenticationSuccessHandler 和 SimpleUrlAuthenticationSuccessHandler 都调用了 getRedirectStrategy().sendRedirect(request, response, targetUrl); 所以他们在我们可以做我们需要的实际重定向之前提交了响应。


推荐阅读