【问题标题】:JSF PrimeFaces ajax request after logout and session invalidation注销和会话失效后的 JSF PrimeFaces ajax 请求
【发布时间】:2016-02-03 21:09:17
【问题描述】:

在我的 Spring Boot1.2.7/JSF2.2.12/PrimeFaces5.2/Tomcat 8 应用程序中,我试图在执行 /logout 的网站上执行 AJAX 调用后重定向到登录页面。

为了做到这一点,我添加了 JsfRedirectStrategy:

/**
* Inspired by <a href=
* "http://stackoverflow.com/questions/10143539/jsf-2-spring-security-3-x-and-richfaces-4-redirect-to-login-page-on-session-tim">StackOverflow.com</a> 
* and by <a href=http://www.icesoft.org/wiki/display/ICE/Spring+Security#SpringSecurity-Step4%3AConfigureYourSpringSecurityredirectStrategy">
* Spring Security 3 and ICEfaces 3 Tutorial</a>.
*
* @author banterCZ
*/
public class JsfRedirectStrategy implements InvalidSessionStrategy {

    final static Logger logger = LoggerFactory.getLogger(JsfRedirectStrategy.class);

    private static final String FACES_REQUEST_HEADER = "faces-request";

    private String invalidSessionUrl;

    /**
     * {@inheritDoc}
     */
    @Override
    public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {

        boolean ajaxRedirect = "partial/ajax".equals(request.getHeader(FACES_REQUEST_HEADER));
        if (ajaxRedirect) {
            String contextPath = request.getContextPath();
            String redirectUrl = contextPath + invalidSessionUrl;
            logger.debug("Session expired due to ajax request, redirecting to '{}'", redirectUrl);

            String ajaxRedirectXml = createAjaxRedirectXml(redirectUrl);
            logger.debug("Ajax partial response to redirect: {}", ajaxRedirectXml);

            response.setContentType("text/xml");
            response.getWriter().write(ajaxRedirectXml);
        } else {
            String requestURI = getRequestUrl(request);
            logger.debug(
                    "Session expired due to non-ajax request, starting a new session and redirect to requested url '{}'",
                    requestURI);
            request.getSession(true);
            response.sendRedirect(requestURI);
        }

    }

    private String getRequestUrl(HttpServletRequest request) {
        StringBuffer requestURL = request.getRequestURL();

        String queryString = request.getQueryString();
        if (StringUtils.hasText(queryString)) {
            requestURL.append("?").append(queryString);
        }

        return requestURL.toString();
    }

    private String createAjaxRedirectXml(String redirectUrl) {
        return new StringBuilder().append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
                .append("<partial-response><redirect url=\"").append(redirectUrl)
                .append("\"></redirect></partial-response>").toString();
    }

    public void setInvalidSessionUrl(String invalidSessionUrl) {
        this.invalidSessionUrl = invalidSessionUrl;
    }

}

这是我的 WebSecurityConfig

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .addFilterBefore(sessionManagementFilter(), AnonymousAuthenticationFilter.class)
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/invite.xhtml").permitAll()
            .antMatchers("/forgotpassword.xhtml").permitAll()
            .antMatchers("/resetpwd.xhtml").permitAll()
            .antMatchers("/admin/**").hasRole(Roles.ROLE_ADMIN.getSpringSecName())
            .antMatchers("/**").authenticated()
            .antMatchers("/actuator/**").permitAll()
            .and()
                .formLogin()
                    .loginPage("/login.xhtml").permitAll()
                    //.failureUrl("/login?error").permitAll()
            .and()
                .logout()
                    .logoutUrl("/logout")
                    .logoutRequestMatcher( new AntPathRequestMatcher("/logout"))
                    .logoutSuccessUrl("/login.xhtml")
                    .invalidateHttpSession(true)
                    .deleteCookies("JSESSIONID")
                    .permitAll();

        http.headers().frameOptions().disable();
     // @formatter:on
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/javax.faces.resource/**");
    }

    @Bean
    public SessionManagementFilter sessionManagementFilter() {
        SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(httpSessionSecurityContextRepository()); 
        sessionManagementFilter.setInvalidSessionStrategy(jsfRedirectStrategy());
        return sessionManagementFilter;
    }

    public HttpSessionSecurityContextRepository httpSessionSecurityContextRepository() {
        return new HttpSessionSecurityContextRepository();
    }

    @Bean
    public JsfRedirectStrategy jsfRedirectStrategy() {
        JsfRedirectStrategy jsfRedirectStrategy = new JsfRedirectStrategy();
        jsfRedirectStrategy.setInvalidSessionUrl("/login.xhtml");
        return jsfRedirectStrategy;
    }

}

这是退出链接:

<div id="LogoutContainer" class="PFTopLinks floatRight boldFont">
    <h:form rendered="#{not empty request.remoteUser}">
        <h:graphicImage name="main/images/pfPush.svg" />
        <h:outputLink value="${pageContext.request.contextPath}/logout">
            <span class="PFDarkText">Logout</span>
        </h:outputLink>
    </h:form>
</div>

问题:现在JsfRedirectStrategy.onInvalidSessionDetected 从未在AJAX JSF 调用中调用,因为SessionManagementFilter.doFilter() 中的request.isRequestedSessionIdValid() 总是返回true。 注销后我有一个org.apache.catalina.session.StandardSessionFacade

的实例

我的代码有什么问题?

【问题讨论】:

    标签: spring jsf primefaces spring-boot tomcat8


    【解决方案1】:

    我用下面的代码重新实现了这个方法(基于这个主题http://forum.primefaces.org/viewtopic.php?f=3&t=33380):

    我添加了AjaxTimeoutPhaseListener相位监听器:

    public class AjaxTimeoutPhaseListener implements PhaseListener {
    
        private static final long serialVersionUID = 2639152532235352192L;
    
        public static Logger logger = LoggerFactory.getLogger(AjaxTimeoutPhaseListener.class);
    
        @Override
        public void afterPhase(PhaseEvent ev) {
        }
    
        @Override
        public void beforePhase(PhaseEvent ev) {
            FacesContext fc = FacesUtils.getContext();
            RequestContext rc = RequestContext.getCurrentInstance();
            HttpServletResponse response = FacesUtils.getResponse();
            HttpServletRequest request = FacesUtils.getRequest();
    
            if (FacesUtils.getExternalContext().getUserPrincipal() == null) {
                if (FacesUtils.getExternalContext().isResponseCommitted()) {
                    // redirect is not possible
                    return;
                }
                try {
                    if (((rc != null && rc.isAjaxRequest())
                            || (fc != null && fc.getPartialViewContext().isPartialRequest()))
                            && fc.getResponseWriter() == null && fc.getRenderKit() == null) {
    
                        response.setCharacterEncoding(request.getCharacterEncoding());
                        RenderKitFactory factory = (RenderKitFactory) FactoryFinder
                                .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
                        RenderKit renderKit = factory.getRenderKit(fc,
                                fc.getApplication().getViewHandler().calculateRenderKitId(fc));
                        ResponseWriter responseWriter = renderKit.createResponseWriter(response.getWriter(), null,
                                request.getCharacterEncoding());
                        responseWriter = new PartialResponseWriter(responseWriter);
                        fc.setResponseWriter(responseWriter);
    
                        FacesUtils.redirect("/login.xhtml");
                    }
                } catch (IOException ex) {
                    StringBuilder error = new StringBuilder("Redirect to the specified page '");
                    error.append("/login.xhtml");
                    error.append("' failed");
                    logger.error(error.toString(), ex);
                    throw new FacesException(ex);
                }
            } else {
                return; // This is not a timeout case . Do nothing !
            }
        }
    
        public PhaseId getPhaseId() {
            return PhaseId.RESTORE_VIEW;
        }
    
    }
    

    还添加了FacesUtils类(从OmniFaceslib中提取):

    public class FacesUtils {
    
        public static Logger logger = LoggerFactory.getLogger(FacesUtils.class);
    
        /**
         * Returns the current faces context.
         * <p>
         * <i>Note that whenever you absolutely need this method to perform a general task, you might want to consider to
         * submit a feature request to OmniFaces in order to add a new utility method which performs exactly this general
         * task.</i>
         * @return The current faces context.
         * @see FacesContext#getCurrentInstance()
         */
        public static FacesContext getContext() {
            return FacesContext.getCurrentInstance();
        }
    
        /**
         * Returns the HTTP servlet response.
         * <p>
         * <i>Note that whenever you absolutely need this method to perform a general task, you might want to consider to
         * submit a feature request to OmniFaces in order to add a new utility method which performs exactly this general
         * task.</i>
         * @return The HTTP servlet response.
         * @see ExternalContext#getResponse()
         */
        public static HttpServletResponse getResponse() {
            return getResponse(getContext());
        }
    
        /**
         * {@inheritDoc}
         * @see Faces#getResponse()
         */
        public static HttpServletResponse getResponse(FacesContext context) {
            return (HttpServletResponse) context.getExternalContext().getResponse();
        }
    
        /**
         * Returns the HTTP servlet request.
         * <p>
         * <i>Note that whenever you absolutely need this method to perform a general task, you might want to consider to
         * submit a feature request to OmniFaces in order to add a new utility method which performs exactly this general
         * task.</i>
         * @return The HTTP servlet request.
         * @see ExternalContext#getRequest()
         */
        public static HttpServletRequest getRequest() {
            return getRequest(getContext());
        }
    
        /**
         * {@inheritDoc}
         * @see Faces#getRequest()
         */
        public static HttpServletRequest getRequest(FacesContext context) {
            return (HttpServletRequest) context.getExternalContext().getRequest();
        }
    
        /**
         * Returns the current external context.
         * <p>
         * <i>Note that whenever you absolutely need this method to perform a general task, you might want to consider to
         * submit a feature request to OmniFaces in order to add a new utility method which performs exactly this general
         * task.</i>
         * @return The current external context.
         * @see FacesContext#getExternalContext()
         */
        public static ExternalContext getExternalContext() {
            return getContext().getExternalContext();
        }
    
        /**
         * Returns the HTTP request context path. It's the webapp context name, with a leading slash. If the webapp runs
         * on context root, then it returns an empty string.
         * @return The HTTP request context path.
         * @see ExternalContext#getRequestContextPath()
         */
        public static String getRequestContextPath() {
            return getRequestContextPath(getContext());
        }
    
        /**
         * {@inheritDoc}
         * @see Faces#getRequestContextPath()
         */
        public static String getRequestContextPath(FacesContext context) {
            return context.getExternalContext().getRequestContextPath();
        }
    
        /**
         * Does a regular or ajax redirect.
         */
        public static void redirect(String redirectPage) throws FacesException {
            checkViewRoot(FacesUtils.getContext(), FacesUtils.getRequestContextPath());
    
            FacesContext fc = FacesUtils.getContext();
            ExternalContext ec = fc.getExternalContext();
    
            try {
                if (ec.isResponseCommitted()) {
                    // redirect is not possible
                    return;
                }
    
                // fix for renderer kit (Mojarra's and PrimeFaces's ajax redirect)
                if ((RequestContext.getCurrentInstance().isAjaxRequest() || fc.getPartialViewContext().isPartialRequest())
                        && fc.getResponseWriter() == null && fc.getRenderKit() == null) {
                    ServletResponse response = (ServletResponse) ec.getResponse();
                    ServletRequest request = (ServletRequest) ec.getRequest();
                    response.setCharacterEncoding(request.getCharacterEncoding());
    
                    RenderKitFactory factory = (RenderKitFactory) FactoryFinder
                            .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
    
                    RenderKit renderKit = factory.getRenderKit(fc,
                            fc.getApplication().getViewHandler().calculateRenderKitId(fc));
    
                    ResponseWriter responseWriter = renderKit.createResponseWriter(response.getWriter(), null,
                            request.getCharacterEncoding());
                    fc.setResponseWriter(responseWriter);
                }
    
                ec.redirect(ec.getRequestContextPath() + (redirectPage != null ? redirectPage : ""));
            } catch (IOException e) {
                logger.error("Redirect to the specified page '" + redirectPage + "' failed");
                throw new FacesException(e);
            }
        }
    
        public static void checkViewRoot(FacesContext ctx, String viewId) {
            if (ctx.getViewRoot() == null) {
                UIViewRoot viewRoot = ctx.getApplication().getViewHandler().createView(ctx, viewId);
                if (viewRoot != null) {
                    ctx.setViewRoot(viewRoot);
                }
            }
        }
    
    }
    

    还在faces-config.xml 中添加了以下几行:

    <lifecycle>
        <phase-listener>com.domain.AjaxTimeoutPhaseListener</phase-listener>
    </lifecycle>
    

    现在一切正常

    【讨论】:

      猜你喜欢
      • 2012-06-27
      • 2021-03-14
      • 1970-01-01
      • 2015-04-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-30
      相关资源
      最近更新 更多