我已经有一个正在生产的Grails Web应用程序,并且有30分钟的会话超时。我们正在运行Tomcat(tcServer)。
当用户通过身份验证并且在某些页面上时,我想要定期轮询对服务器的ajax请求,这些请求不会延长这个30分钟的会话超时 - 这样我们的会话超时就不会被阻止。
问题类似于this unanswered asp.net question,但在Java / Tomcat领域中没有任何答案可以做到这一点。
如何在不重置tomcat的会话超时的情况下执行经过身份验证的AJAX请求?
我是否可以使用某种过滤器或网址匹配机制来排除延长会话超时的请求?
答案 0 :(得分:4)
我会选择一个Grails过滤器来做类似于The-MeLLeR提议的事情而没有在所有会话中进行不必要的循环:
class AjaxTimeoutFilters {
int sessionTimeout = 30 * 60 * 1000
private static final String TIMEOUT_KEY = 'TIMEOUT_KEY'
def filters = {
all(controller:'*', action:'*') {
before = {
if (request.xhr) {
Long lastAccess = session[TIMEOUT_KEY]
if (lastAccess == null) {
// TODO
return false
}
if (System.currentTimeMillis() - lastAccess > sessionTimeout) {
session.invalidate()
// TODO - render response to trigger client redirect
return false
}
}
else {
session[TIMEOUT_KEY] = System.currentTimeMillis()
}
true
}
}
}
}
会话超时应该是依赖注入的,或者与web.xml中的值保持同步。
还有两个问题。一种情况是存在Ajax请求但没有先前的非Ajax请求(lastAccess == null)。另一种方法是如何将浏览器重定向到登录页面,或者在没有非Ajax活动30分钟后出现Ajax请求时需要去的地方。您必须呈现JSON或客户端检查的其他响应,以确定它已超时并执行客户端重定向。
答案 1 :(得分:0)
不可能......
一个选项如下:
1)创建一个javax.servlet.Filter并在会话中存储最后一个(非ajax)综合浏览量的时间戳。
2)创建一个javax.servlet.http.HttpSessionListener来存储所有活动会话。
3)使用后台线程使所有过期的会话无效。
示例代码:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class LastAccessFilter implements Filter, HttpSessionListener {
private static final Object SYNC_OBJECT = new Object();
private static final String LAST_ACCESSED = "lastAccessed";
private boolean backgroundThreadEnabled;
public void destroy() {
synchronized (SYNC_OBJECT){
backgroundThreadEnabled = false;
SYNC_OBJECT.notifyAll();
}
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
if (req instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) req;
if(!isAjax(httpServletRequest)){
httpServletRequest.getSession().setAttribute(LAST_ACCESSED, System.currentTimeMillis());
}
}
chain.doFilter(req, resp);
}
public static boolean isAjax(request) {
return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
}
public void init(FilterConfig config) throws ServletException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (LastAccessFilter.this.backgroundThreadEnabled) {
synchronized (SYNC_OBJECT) {
try {
SYNC_OBJECT.wait(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (LastAccessFilter.this.backgroundThreadEnabled) {
HttpSession[] sessions;
synchronized (activeSessions){
sessions = activeSessions.toArray(new HttpSession[activeSessions.size()]);
}
cleanupInactiveSessions(sessions);
}
}
}
}
private void cleanupInactiveSessions(HttpSession[] sessions) {
for (HttpSession session : sessions) {
Object lastAccessedObject = session.getAttribute(LAST_ACCESSED);
if(lastAccessedObject == null) continue;
long lastAccessed = (Long)lastAccessedObject;
if(System.currentTimeMillis() > (lastAccessed + 1800000)){//30 Minutes
session.invalidate();
}
}
}
});
t.setDaemon(true);
this.backgroundThreadEnabled = true;
t.start();
}
private final List<HttpSession> activeSessions = new ArrayList<HttpSession>();
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
synchronized (activeSessions) {
this.activeSessions.add(httpSessionEvent.getSession());
}
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
synchronized (activeSessions) {
this.activeSessions.remove(httpSessionEvent.getSession());
}
}
}