返回在 jsf 中销毁的会话的欢迎页面 [重复]

Posted

技术标签:

【中文标题】返回在 jsf 中销毁的会话的欢迎页面 [重复]【英文标题】:returning to welcome page on session destroyed in jsf [duplicate] 【发布时间】:2012-02-10 17:28:30 【问题描述】:

我已经实现了一个会话监听器。我希望当用户在会话被破坏后尝试使用该站点时,他应该被重定向到欢迎页面(登录页面)。 我已经通过loging?faces-redirect=true 尝试过此操作,但我必须单击两次才能真正重定向到登录页面。 此外,当会话在欢迎页面(登录页面)上到期时。应用程序崩溃,如以下错误所示:

    WARNING: StandardWrapperValve[Faces Servlet]: PWC1406: Servlet.service() for servlet Faces Servlet threw exception
javax.faces.application.ViewExpiredException: viewId:/loginpage.xhtml - View /loginpage.xhtml could not be restored.
        at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:205)
        at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
        at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:116)
        at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
        at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1539)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:98)
        at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:91)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:162)
        at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:330)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
        at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:174)
        at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:828)
        at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:725)
        at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1019)
        at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:225)
        at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
        at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
        at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
        at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
        at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
        at java.lang.Thread.run(Thread.java:662)

我在 netbeans 上使用 primefaces 3.0、glassfish 3.1.1 谢谢。

【问题讨论】:

我认为你应该向我们展示你的会话监听器 【参考方案1】:

如果用户未通过身份验证,您可以使用 servlet 过滤器或 JSF 阶段侦听器重定向到登录页面。

【讨论】:

JSF 相位监听器在这种情况下并不是一个非常理想的解决方案。应该使用 Servlet 过滤器。【参考方案2】:

我们在应用程序中遇到了类似的问题。以下是我们最终使用的解决方案。我们使用阶段监听器在会话过期的情况下重定向到登录页面(并且它们不在登录页面上)。然后,我们使用自定义视图处理程序来防止用户在登录页面上遇到过期会话。基本上,如果我们在登录页面上看到会话已过期,我们会创建一个新的。

注意:针对特定用例需要更新的部分代码已标记。 我们针对我们在网上发现的特定问题汇总了几个示例,从而提出了这种方法。部分参考资料如下:

http://www.gregbugaj.com/?p=164

https://***.com/a/6816513/2212458

https://***.com/a/4992869/2212458

这里是阶段监听器,负责确保访问者有会话,如果没有会话(例如过期时),则将其转发到登录页面。它还执行其他 2 项检查。它确保他们是否有会话,他们是否已通过身份验证(登录),并确保他们有权访问他们正在点击的页面。

import javax.faces.application.NavigationHandler;
import javax.faces.context.FacesContext;
import javax.faces.event.*;
import javax.servlet.http.HttpSession;

/**
 * A phase listener.  Runs after the restore view phase.  Makes sure that the user is logged on
 * to view any page other than the login page.
 */
public class AuthorizationListener implements PhaseListener

    /**
     * Called after phase executes.  Makes sure we are logged in if we are not on the login page.
     * @param phaseEvent
     */
    @Override
    public void afterPhase(PhaseEvent phaseEvent)
    
        // get page we are on
        FacesContext facesContext = phaseEvent.getFacesContext();
        String currentPage = facesContext.getViewRoot().getViewId();

        // determine if we are on the login page
        boolean isLoginPage = currentPage.contains("login"); <--- CHANGE
        if (isLoginPage)
        
            return;
        

        // get session - do not create one if it does not exist
        HttpSession session = (HttpSession) facesContext.getExternalContext().getSession(false);

        // no session is present
        if(session==null)
        
            NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
            nh.handleNavigation(facesContext, null, "login?faces-redirect=true&reason=expired"); <--- CHANGE
            return;
        

        // if not logged in send to login page
        if (USER IS NOT LOGGED IN) <--- CHANGE
        
            NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
            nh.handleNavigation(facesContext, null, "login?faces-redirect=true&reason=expired"); <--- CHANGE
            return;
        

        // they are logged in, make sure they have rights to page they are visiting
        if (USE DOES NOT HAVE RIGHTS TO THE PAGE THEY ARE VISITING) <--- CHANGE
        
            // user does not have privilege to go to this page
            NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
            nh.handleNavigation(facesContext, null, accessDenied); <--- CHANGE
        
    

    /**
     * Called before phase executes.  Does nothing.
     * @param phaseEvent the phase event
     */
    @Override
    public void beforePhase(PhaseEvent phaseEvent)
    
        // intentionally left blank
    

    /**
     * Identifies the phase we want to listen and respond to.
     * @return the phase
     */
    @Override
    public PhaseId getPhaseId()
    
        return PhaseId.RESTORE_VIEW;
    

这是负责在登录页面上停止会话过期的自定义视图处理程序。

import javax.faces.application.ViewHandler;
import javax.faces.application.ViewHandlerWrapper;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import java.io.IOException;

/**
 * This class adds additional behavior to the facelet view handler.  Specifically it
 * prevents the user from experiencing session/view timeout errors at the login screen.
 */
public class CustomViewHandler extends ViewHandlerWrapper

    /** The default view handler we are adding extra behavior to. */
    private ViewHandler wrapped;

    /**
     * Constructor.
     * @param wrapped the wrapped handler.  Ref.
     */
    public CustomViewHandler(ViewHandler wrapped)
    
        super();
        this.wrapped = wrapped;
    

    /**
     * Expose the wrapped handler (required by base class).
     * @return the handler.  Ref.
     */
    @Override
    public ViewHandler getWrapped()
    
        return wrapped;
    

    /**
     * Called when a view is restored.  Prevents expiration on login page.
     * @param facesContext the context for this request
     * @param viewId the view identifier for the current request
     * @return the restored view
     */
    @Override
    public UIViewRoot restoreView(FacesContext facesContext, String viewId)
    
        // have the wrapped handler restore the view
        UIViewRoot root = super.restoreView(facesContext, viewId);

        // if there was no view to restore (maybe because it expired)
        if (root == null)
        
            // if the view expired on the login page make a new view, don't allow login page to expire
            if ( viewId.contains("login") ) <--- CHANGE
            
                // create a new view
                // for some reason the starting slash is required else we get errors in server log about not finding the page
                root = createView(facesContext, "/" + "login"); <--- CHANGE
                // saves view - without this session never gets created so we will just keep hitting this code
                facesContext.renderResponse();
            
        

        return root;
    

    /**
     * Called when a view is rendered.  Does nothing but log a message.
     * @param context the context for this request
     * @param viewToRender the view to render
     * @throws IOException thrown if an input/output error occurs in wrapped handler
     */
    @Override
    public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException
    
        super.renderView(context, viewToRender);
    

需要更改配置文件才能使用此代码。

faces-config.xml 的补充

<view-handler>PACKAGE.CustomViewHandler</view-handler> <--- CHANGE
<lifecycle>
    <phase-listener>PACKAGE.AuthorizationListener</phase-listener> <--- CHANGE
</lifecycle>

对 web.xml 的补充

<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/login.xhtml?reason=expired</location> <--- CHANGE
</error-page>-<session-config><session-timeout> 10 </session-timeout></session-config>
<listener><listener-class> com.sun.faces.config.ConfigureListener </listener-class></listener>

【讨论】:

以上是关于返回在 jsf 中销毁的会话的欢迎页面 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

JSF 中的 ViewExpiredException [重复]

检查会话是不是存在 JSF

会话范围和 jsf 重定向

JSF 2.0 在整个会话中从浏览器和以编程方式设置区域设置 [重复]

从 JSF 页面获取请求和会话参数和属性

如果会话无效,请禁用浏览器后退按钮 [重复]