session的应用(转载)

 我想要的效果是不关闭浏览器就不会断直到失效时间,而一旦关闭浏览器就退出了。

前半句可以做到,但需要用类似 AJAX 的方法反复向服务器发请求,以保持 session 的活性,避免服务器端 session 超时。

后半句比较困难,你可以在 onunload 事件里向服务器发个请求来关闭 session,但这样做问题很多,比如,当有多个窗口打开时,关闭其中某一个,怎么办?总不能这样就关闭 session 了吧?可是你怎么判断要关闭的是“最后一个窗口”呢?……

其实以上说的这些都不是关键的。你要解决的关键问题是:如果客户端的窗口都关闭了,服务器上的 session 还未失效,此时同一个用户账号通过另外一个 session 登录进来了,怎么办?

方法1:不管它,允许登录,继续工作。这样处理的问题是,同一个用户账号可能同时登录多个 session,也许你的应用系统不希望这样使用(比如希望限制用户数)。

方法2:就是你开始说的,检查出该用户的 session 已经存在,就拒绝登录。这样处理的问题是,垃圾 session 会导致用户在一段时间内无法登录。这显然是无法接受的。

方法3:既然你已经能检测出该用户已经登录,当然也就能得到那个 session 对象,你可以让那个 session 失效,然后在新的 session 下继续工作。这样做的问题是,如果原来的 session 并不是僵死的垃圾,那个用户窗口还在继续工作,那么他会觉得“被踢出”了。但这种结果总归是比前一种结果好,而且也能满足“限制用户数”的需求。

方法4:查出已经登录的那个 session 后,通过“URL转发”把客户端引导到那个 session 里面去,而把新的这个 session 作废。这样的效果是,新登录的用户感觉是在原来那个 session 的基础上继续工作。这种处理方法对于“误操作关闭所有窗口后重新登录继续工作”最有效,但如果真的是“两个不同的人从不同的机器上用同一个账号登录”,可能会互相干扰,可以看做是对“限制用户数”需求的另一种处理方式。

 

 

你是在 application 里保存了所有登录用户名。如果你把所有登录的 session 都保存在 application 里,不就 OK 了? :)

索性我给你一段源程序吧,里面实现了我前面说的 4 种策略,如果你有耐心读完的话,也许会有些帮助。

这是一个 Filter,它在自己的实例中保存了全部登录成功的 session。由于一个 Web-application 中每个 Filter 只有一个实例,所以这种方法跟你在 application 对象中保存 TreeSet 是一样的。

Java code
 import java.io.IOException;
import java.util.Hashtable;
import java.util.Iterator;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;/**
 * GateFilter 用于对 web-application 中的 request 进行过滤检查。
 * 
 * <ol type="1">
 * <p>它解决两个方面的问题:</p>
 * 
 * <li>检查当前用户是否已经登录
 * 
 * <p>如果尚未登录,将通过重定向把浏览器引导到指定的登录画面。</p>
 * 
 * <li>当同一个用户帐号重复登录时,按指定的策略进行处理
 * <pre>
 * default - 缺省情况下,各自使用自己的 session;
 * join    - 后一个用户加入前面的 session(自己的 session 要废掉);
 * replace - 后一个用户正常使用,前一个用户的 session 要被废掉;
 * deny    - 后一个用户会被拒绝;
 * </pre>
 * </ol>
 * 
 * <p>需要为 GateFilter 指定 3 个参数:
 * 
 * <pre>
 * userSessionKey - 在 session 中保存用户登录对象的 key;
 * loginUri       - 用于提供登录画面和登录验证操作的 uri;
 * sessionPolicy  - 当同一个用户帐号重复登录时的处理策略;
 * </pre>
 */
public class GateFilter implements Filter {
    private Log log = LogFactory.getLog(getClass());    public final static String SESSION_KEY_RECORDED = "GateFilter.recorded";    private String userSessionKey;
    private String loginUri;
    private int policy;
    private Hashtable<String, HttpSession> sessions;    public void init(FilterConfig config) throws ServletException {
        log.trace("init()");        userSessionKey = config.getInitParameter("userSessionKey");
        loginUri = config.getInitParameter("loginUri");        String sessionPolicy = config.getInitParameter("sessionPolicy");
        policy = 0;
        if ("join".equalsIgnoreCase(sessionPolicy)) {
            policy = 1;
        } else if ("replace".equalsIgnoreCase(sessionPolicy)) {
            policy = 2;
        } else if ("deny".equalsIgnoreCase(sessionPolicy)) {
            policy = 3;
        }        if (policy > 0)
            sessions = new Hashtable<String, HttpSession>();
    }    public void destroy() {
    }    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.trace("doFilter()");        if (!(request instanceof HttpServletRequest)) {
            chain.doFilter(request, response);
            return;
        }        HttpServletRequest httpRequest = (HttpServletRequest)request;
        HttpServletResponse httpResponse = (HttpServletResponse)response;
        String requestURI = httpRequest.getRequestURI();
        // 如果是登录请求,就不做额外处理
        if (requestURI.endsWith(loginUri)) {
            chain.doFilter(request, response);
            return;
        }        // 检查 session 中是否记录了有效的 user 对象
        HttpSession session = httpRequest.getSession();
        Object user = session.getAttribute(userSessionKey);
        if (user == null) {
            // 如果尚未登录成功,则重定向到登录画面
            String contextPath = httpRequest.getContextPath();
            String target = httpResponse.encodeRedirectURL(requestURI);
            httpResponse.sendRedirect(contextPath + loginUri + "?target=" + target);
            return;
        }        // 如果已经记录过,就不再重复处理了
        Boolean bRecorded = (Boolean)session.getAttribute(SESSION_KEY_RECORDED);
        if (bRecorded != null) {
            chain.doFilter(request, response);
            return;
        }        // 缺省情况下,重复登录的用户各自使用自己的 session
        if (policy == 0) {
            chain.doFilter(request, response);
            return;
        }        // 查找已经存在的包含相同用户的 session
        HttpSession existSession = null;
        Iterator it = sessions.values().iterator();
        while (it.hasNext()) {
            HttpSession ses = (HttpSession)it.next();
            try {
                Object existUser = ses.getAttribute(userSessionKey);
                if (user.equals(existUser)) {
                    existSession = ses;
                }
            } catch (RuntimeException e) {
                // 如果发生异常,说明 ses 已经失效
                sessions.remove(ses.getId());
            }
        }        // 后登录的用户加入到前面已经登录的 session 中
        if (policy == 1 && existSession != null) {
            // 同一用户帐号前面已经登录过,废除当前的 session,重用找到的这个
            // 通过重定向让浏览器重新请求同一个 URL,并通过 cookie 告知浏览器使用新的 JSESSIONID
            session.invalidate();
            String url = httpRequest.getRequestURL().toString();
            // addCookie() 生成的 header 含有引号,导致 cookie 设置失败
            //httpResponse.addCookie(new Cookie("JSESSIONID", existSession.getId() + "; Path=/"));
            // 只好使用 setHeader() 来设置 cookie
            httpResponse.setHeader("Set-Cookie", "JSESSIONID=" + existSession.getId() + "; Path=/");
            httpResponse.sendRedirect(url);
            return;
        }        // 后登录的用户继续使用,把前面的用户踢出
        else if (policy == 2 && existSession != null) {
            sessions.remove(existSession.getId());
            existSession.invalidate();
        }        // 阻止后登录的用户
        else if (policy == 3 && existSession != null) {
            session.invalidate();
            String contextPath = httpRequest.getContextPath();
            httpResponse.sendRedirect(contextPath);
            return;
        }        // 把当前这个 session 记录下来,其它一切照旧
        sessions.put(session.getId(), session);
        session.setAttribute(SESSION_KEY_RECORDED, true);        chain.doFilter(request, response);
        return;
    }}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值