Web项目防止同一账号在不同session下重复登录

一、session简介
浏览器在请求服务器时,服务器都会创建一个session,session负责浏览器与服务器之间的会话。session的存在是为了维护浏览器和服务器之间交互时保留一些交互数据,例如用户行为或者用户信息等。每个会话都有自己的唯一标识,叫做sessionId。这个流程如下:
  1. 浏览器请求服务器。
  2. 服务器检查浏览器是否携带sessionId,如果未携带sessionId或者携带一个不存在的sessionId,服务器生成一个session,并将sessionId返回给浏览器。
  3. 浏览器将sessionId存放在cookie中。
  4. 浏览器接下来访问该服务器时,就会带着sessionId去访问服务器,服务器都会找到处理该会话的session对象。直到session过期或者session被销毁。

session有自己的过期时间(这个时间是服务气端设置的),如果浏览器长时间未访问服务器,session会自动中断。(session的存在是因为Http协议是一种无状态协议,每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录)。

二、用户身份验证
几乎每一个网站针对不同身份的用户会有不同的呈现,例如:游客、登录用户、管理员。这种情况就需要我们在基于session的基础上再实现一套用户身份的认证。这种身份认证通常是网站在需要用户信息认证时,用户输入用账号,密码来实现的。
三、不同session下登录同一账号
用在用不同浏览器访问同一个网站时,登录同一个用户账号,这种情况下就会出现“不同session下重复登录账号”。在现实中的业务中,很多时候,我们是不允许用户在多处登录同一账号的。

处理这个需求的思路:

  1. 每当服务器端生成一个新的session时,将sessionId和session对象分别作为key和value存储起来(可用一个全局的map,也可以用redis存储)。
  2. 用户登录账号时,检查服务器端的全部session,检查是否有该用户标识的session,如果有,就将之前的session关闭。
  3. 关闭之前的session后,用户的账号信息(或者能区分用户身份的唯一标识)以参数的形式放在当前的session中。方便下次登录检测。

具体实现
实现一个session管理类。

import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MySessionContext {

    private static MySessionContext instance;
    private HashMap<String,HttpSession> sessionMap;

	//创建存储session的map集合
    private MySessionContext() {
        sessionMap = new HashMap<String,HttpSession>();
    }

	//获取处理session的实例
    public static MySessionContext getInstance() {
        if (instance == null) {
            instance = new MySessionContext();
        }
        return instance;
    }

	//添加一个session到map
    public synchronized void addSession(HttpSession session) {
        if (session != null) {
            sessionMap.put(session.getId(), session);
        }
    }

	//删除一个session到map
    public synchronized void delSession(HttpSession session) {
        if (session != null) {
            sessionMap.remove(session.getId());
        }
    }

	//根据sessionId获取一个session对象
    public synchronized HttpSession getSession(String sessionID) {
        if (sessionID == null) {
            return null;
        }
        return sessionMap.get(sessionID);
    }

    //检查是否有存在该用户名的session
    public HttpSession checkExist(String username){

        Set<Map.Entry<String,HttpSession>> entrySet = sessionMap.entrySet();

        for(Map.Entry<String,HttpSession> entry : entrySet){
            HttpSession session = entry.getValue();

            Object usernameValue = session.getAttribute("usernameKey");

            if(usernameValue != null && usernameValue.toString().equals(username)){
                return entry.getValue();
            }
        }

        return null;
    }

}

实现一个session监听类:


import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class SessionListener implements HttpSessionListener {


    private MySessionContext myc = MySessionContext.getInstance();

	//当浏览器与服务器之间的session被创建时,存储该session。
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        HttpSession session = httpSessionEvent.getSession();
        myc.addSession(session);
    }

	//当session被销毁时,删除该session。
    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        HttpSession session = httpSessionEvent.getSession();
        myc.delSession(session);
    }
}

将该session监听类注册到web.xml的监听器列表:

<listener>
        <listener-class>com.msmk.cloud.listener.SessionListener</listener-class>
</listener>

在用户登录验证通过后调用下面这个方法处理session:


  /**
     * 专门用于处理单点登录的方法,如果用户用同一个账号登录过了,将之前的session销毁。
     * @param request
     */
    public void handleSession(HttpServletRequest request){

        User user = userService.getCurrent();

        if(user == null){
            return;
        }

		//作为区分用户的唯一标识。
        String usernameKey = "";

        //如果用户名不为空
        if(!StringUtils.isEmpty(user.getUsername())){
            usernameKey = user.getUsername();
        }

        //如果用户民为null,那就是第三方登录,使用otherId
        if(StringUtils.isEmpty(user.getUsername()) && (!StringUtils.isEmpty(user.getOtherUid()))){
            usernameKey = user.getOtherUid();
        }else{
            if(!StringUtils.isEmpty(user.getOpenid())){
                usernameKey = user.getOtherUid();
            }
        }

        //获取session管理实例。
        MySessionContext myc= MySessionContext.getInstance();
        HttpSession httpSession = myc.checkExist(usernameKey);

        //如果sessionMap中存在该用户标识的session,将前一个session关闭。
        if(httpSession != null){
            httpSession.invalidate();
        }

        //将代表用户的唯一标识,大部分是username,由于第三方登录,有时也是otherId,或者openId
        HttpSession sessionCurrent = request.getSession();
        sessionCurrent.setAttribute("usernameKey",usernameKey);
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值