java 如何在 Web 应用程序中实现自动注销?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10818473/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to implement an automatic logout in a web application?
提问by Bevor
Our web application runs with JBoss 7.1.1 and Java (JPA2 and RichFaces4). At the moment the problem is that the application just gets stuck if the user is logged in but doesn't do anything inside the application for some time (probably caused due to session timeout). Then the user has to load the web application again, which doesn't look very professional.
我们的 Web 应用程序使用 JBoss 7.1.1 和 Java(JPA2 和 RichFaces4)运行。目前的问题是,如果用户登录但在应用程序内没有做任何事情一段时间(可能是由于会话超时引起的),应用程序就会卡住。然后用户不得不再次加载web应用程序,这看起来不是很专业。
Can you give me a hint, how an automatic logout could be implemented using the mentioned technologies?
您能给我一个提示,如何使用上述技术实现自动注销?
[UPDATE]
I tried a lot of possibilities but none of them work properly. My thought is to implement a SessionTimeoutListener which knows when the session gets expired:
[更新]
我尝试了很多可能性,但没有一个能正常工作。我的想法是实现一个 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);
}
}
This works. But then all the problems appear: I cannot redirect from it because FacesContext is null in there. I cannot fire push eventsbecause I get some exceptions like NameNotFoundException or similar (I tried a lot but it seemed that firing events doesn't work out of this too). Then I tried a4j:poll on ApplicationContext.isAutoLoggedOut() but this doesn't work either, because if I execute poll events, the session would never expire. I always come to a dead end. If I could redirect somehow from SessionTimeoutListener, this would be the solution.
这有效。但是随后出现了所有问题:我无法从它重定向,因为 FacesContext 在那里为空。我无法触发推送事件,因为我收到了一些异常,如 NameNotFoundException 或类似的异常(我尝试了很多,但似乎触发事件也不能解决这个问题)。然后我在 ApplicationContext.isAutoLoggedOut() 上尝试了 a4j:poll 但这也不起作用,因为如果我执行轮询事件,会话将永远不会过期。我总是走到死胡同。如果我可以从 SessionTimeoutListener 以某种方式重定向,这将是解决方案。
[POSSIBLE SOLUTION]
I'm now satisfied with a logout which is executed when I click on any button inside the view after session is expired. This current solution is only rudimentary and not applicable for production yet, but this solution works and I will build on it:
I use the upper SessionTimeoutListener. Moreover I use a PhaseListener which is being invoked after the SessionTimeoutListener, so if the session expires, the SessionTimeoutListener will be invoked and destroys the session but the SessionTimeoutPhaseListener still has the FacesContext. So I can redirect there to logout page:
[可能的解决方案]
我现在对在会话过期后单击视图内的任何按钮时执行的注销感到满意。这个当前的解决方案只是初级的,还不适用于生产,但这个解决方案有效,我将在它的基础上进行构建:我使用上层 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;
}
}
The ApplicationContext is a class with @ApplicationScoped which stores the boolean variables, but this has to be changed, because it would affect every user who currently works with the application. I think about some "ThreadLocal context" to solve that. I still have to distinguish between auto logout and manual logout. The listener is invoked in both cases. Although this solution works at the moment, the redirection in PhaseListener is also tricky, because it would be invoked over and over again by JSF (which causes redirection loop errors in browser), if I wouldn't set "autoLoggedOut" to false.... as I said, only rudimentary, but using the PhaseListener is probably the only suitable solution.
ApplicationContext 是一个带有 @ApplicationScoped 的类,用于存储布尔变量,但这必须更改,因为它会影响当前使用该应用程序的每个用户。我想了一些“ThreadLocal 上下文”来解决这个问题。我仍然需要区分自动注销和手动注销。在这两种情况下都会调用侦听器。虽然这个解决方案目前有效,但 PhaseListener 中的重定向也很棘手,因为如果我不将“autoLoggedOut”设置为 false,它会被 JSF 一遍又一遍地调用(这会导致浏览器中的重定向循环错误)。 .. 正如我所说,只是基本的,但使用 PhaseListener 可能是唯一合适的解决方案。
采纳答案by kostja
If you want to handle the ViewExpiredException, you could add the following setting to your web.xml
如果要处理 ViewExpiredException,可以将以下设置添加到 web.xml
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/login.jsf</location>
</error-page>
Better yet, use a PhaseListenertio check if the User is still available in the Session.
更好的是,使用PhaseListenertio 检查用户是否在会话中仍然可用。
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("User")
If not - navigate to the login page.
如果没有 - 导航到登录页面。
FacesContext.getCurrentInstance().getApplication().getNavigationHandler().handleNavigation(FacesContext.getCurrentInstance(), null, "login");
回答by mprabhat
There are two ways that you can do this:
有两种方法可以做到这一点:
1 Use pull mechanism Every 5 seconds or so, keep asking server if session is valid or not, if server says yes valid, sleep for specified period of time, if server says invalid, show message to user, and logout.
1 使用拉取机制 每 5 秒左右,不断询问服务器会话是否有效,如果服务器说是有效,则休眠指定的时间,如果服务器说无效,则向用户显示消息,然后注销。
2 Use push mechanism
Use a4j:push
, so in your server you have a TimerTask
which runs says every 5 seconds to check if session is valid or not, if it sees session is valid, sleep, if invalid, send message to user and then logout.
2 使用推送机制使用a4j:push
,所以在您的服务器中,您有一个TimerTask
每 5 秒运行一次,以检查会话是否有效,如果它看到会话有效,则休眠,如果无效,则向用户发送消息然后注销。
Looks like RichFaces4 online demo is not working, though check this link
看起来 RichFaces4 在线演示不起作用,但请查看此链接
I am not sure if RichFaces has something like Primefaces Idle Monitor
我不确定 RichFaces 是否有像Primefaces Idle Monitor这样的东西
Another solution is to use Custom HttpSessionListener
另一种解决方案是使用自定义 HttpSessionListener
回答by Luiggi Mendoza
You have to configure the session timeout in your web.xml, the value is in minutes and can't be less than 1:
您必须在 web.xml 中配置会话超时,该值以分钟为单位并且不能小于 1:
<session-config>
<!-- The application will have 15 minutes for session timeout -->
<session-timeout>15</session-timeout>
</session-config>
You should add a filter that will check if the session has expired:
您应该添加一个过滤器来检查会话是否已过期:
<filter>
<filter-name>SessionTimeoutFilter</filter-name>
<filter-class>edu.home.TimeoutFilter</filter-class>
</filter>
The filter will look like this:
过滤器将如下所示:
public class TimeoutFilter implements Filter {
private String startPage = "index.xhtml";
public void init(FilterConfig filterConfig) throws ServletException {}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse)) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
if (!isValidSession(httpServletRequest)) {
String timeoutUrl = httpServletRequest.getContextPath() + "/"
+ startPage;
//redirects to the first page
httpServletResponse.sendRedirect(timeoutUrl);
}
filterChain.doFilter(request, response);
}
}
private boolean isValidSession(HttpServletRequest httpServletRequest) {
return (httpServletRequest.getRequestedSessionId() != null) &&
httpServletRequest.isRequestedSessionIdValid();
}
}
回答by naspinski
Since it is web, you could use some javascript:
由于它是网络,您可以使用一些javascript:
var timeout;
var timeoutTime = 294000;
$().ready(function() {
$(".loading").bind("ajaxSend", function() {
timeout = setTimeout("TimedOut()", timeoutTime);
}
});
function TimedOut() {
//here I do an async call to test if a user is timed out
var isTimedOut = yourMethodToTest();
if(isTimedOut) { //do whatever you need to do here;
alert("session timed out");
}
else { //restart the checker
timeout = setTimeout("TimedOut()",timeoutTime);
}
}