2015-11-03 99 views
1

在我的春節Boot1.2.7/JSF2.2.12/PrimeFaces5.2/Tomcat的8應用程序,我想實現重定向一個網站上的AJAX調用後登錄頁面,在這裏/註銷了已執行。JSF PrimeFaces 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調用因爲request.isRequestedSessionIdValid()SessionManagementFilter.doFilter()總是返回true。 有登錄後,我有一個org.apache.catalina.session.StandardSessionFacade

我的代碼有什麼問題嗎?

回答

3

我已經重新實現用下面的代碼本的方法(基於本主題http://forum.primefaces.org/viewtopic.php?f=3&t=33380):

我已經加入相監聽器:

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類(從OmniFaces LIB中提取):

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> 

現在一切工作正常