我们的 Web 应用程序使用 JBoss 7.1.1 和 Java(JPA2 和 RichFaces4)运行。目前的问题是,如果用户登录但在一段时间内没有在应用程序内执行任何操作(可能是由于会话超时引起的),应用程序就会卡住。然后用户必须再次加载Web应用程序,这看起来不太专业。
您能否给我一个提示,如何使用上述技术实现自动注销?
[更新]
我尝试了很多可能性,但没有一个能正常工作。我的想法是实现一个知道会话何时过期的 SessionTimeoutListener:
@Logged
@WebListener
@Named
public class SessionTimeoutListener implements HttpSessionListener
{
@Inject
private Logger logger;
@Override
public void sessionCreated(HttpSessionEvent event)
{
// not used.
}
@Override
public void sessionDestroyed(HttpSessionEvent event)
{
logger.info("Session destroyed.");
ApplicationContext.setAutoLoggedOut(true);
}
}
这行得通。但是随后出现了所有问题:我无法从中重定向,因为 FacesContext 在那里为空。我无法触发推送事件,因为我遇到了一些异常,例如 NameNotFoundException 或类似的(我尝试了很多,但似乎触发事件也无法解决这个问题)。然后我在 ApplicationContext.isAutoLoggedOut() 上尝试了 a4j:poll 但这也不起作用,因为如果我执行轮询事件,会话将永远不会过期。我总是走入死胡同。如果我可以从 SessionTimeoutListener 以某种方式重定向,这将是解决方案。
[可能的解决方案]
我现在对在会话过期后单击视图内的任何按钮时执行的注销感到满意。当前的解决方案只是初步的,尚不适用于生产,但此解决方案有效,我将在此基础上进行构建:我使用上层 SessionTimeoutListener。此外,我使用了在 SessionTimeoutListener 之后调用的 PhaseListener,因此如果会话到期,将调用 SessionTimeoutListener 并销毁会话,但 SessionTimeoutPhaseListener 仍然具有 FacesContext。所以我可以在那里重定向到注销页面:
public class SessionTimeoutPhaseListener implements PhaseListener
{
private static final long serialVersionUID = -8603272654541248512L;
@Override
public void beforePhase(PhaseEvent event)
{
//not used.
}
@Override
public void afterPhase(PhaseEvent event)
{
FacesContext facesContext = event.getFacesContext();
if (ApplicationContext.isAutoLoggedOut())
{
ApplicationContext.setAutoLoggedOut(false);
try
{
facesContext.getExternalContext().redirect("./logout.xhtml");
}
catch (IOException e)
{
}
}
}
@Override
public PhaseId getPhaseId()
{
return PhaseId.RESTORE_VIEW;
}
}
ApplicationContext 是一个带有 @ApplicationScoped 的类,它存储布尔变量,但这必须更改,因为它会影响当前使用该应用程序的每个用户。我想一些“ThreadLocal context”来解决这个问题。我仍然必须区分自动注销和手动注销。在这两种情况下都会调用侦听器。虽然这个解决方案目前有效,但 PhaseListener 中的重定向也很棘手,因为如果我不将“autoLoggedOut”设置为 false,JSF 会一遍又一遍地调用它(这会导致浏览器中的重定向循环错误)。 .. 正如我所说,只是初步的,但使用 PhaseListener 可能是唯一合适的解决方案。