Java使用HttpSessionListener同一账号登录人数限制,踢出功能

前言:

参考:http://blog.csdn.net/liuyuan185442111/article/details/45422779

如有疑问可进行留言,我会抽空回复

1.因为公司的项目提出需求,要求系统用户同一账号登录的session次数为3个,超出将踢出第一个用户,开始考虑到了shiro、security这两种安全框架,百度了一堆乱代码无法解决我的需求,由于时间有限和本身对这两种框架不是太熟练所以,我决定使用session监听器去实现我的项目需求,不多说看代码吧。



一、编写class类实现监听

提示:首先要实现 HttpSessionListener接口重写它的两个实现方法一个为创建一个为销毁
package com.maple.scof.member.common;

import com.maple.scof.business.acnotation.Id;
import com.maple.scof.business.entity.MemberEntity;
import com.maple.scof.member.util.EnumUtil;
import com.maple.scof.member.util.SessionUtil;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletContext;
import javax.servlet.http.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class MemberSessionListener implements HttpSessionListener{

    @Override
    public void sessionCreated(HttpSessionEvent se) {

    }


    /**
     * 执行session 销毁方法
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        HttpSession session = event.getSession();
        ServletContext application = session.getServletContext();

        //第一步将当前登录用户的对象取出来
        MemberEntity member=(MemberEntity) session.getAttribute(EnumUtil.sessionKey.USER.getValue());
        if(member == null)
            return;
        //从ServletContext取出当前登录用户对应的map 对应多个session
        Map<String,List<HttpSession>>  userSessionLoginKeyMap =  (Map<String,List<HttpSession>>)application.getAttribute(member.getMemberId()+"");
        if(userSessionLoginKeyMap != null){
            //取出登录用户对应的session集合
            List<HttpSession> list = userSessionLoginKeyMap.get(member.getMemberId()+"");
            if(list != null && list.size() > 0){

                //如果当前map里面的登录session个数等于1那么就将对应的map和当前session给删除
                if(list.size() == 1){
                    session.removeAttribute(EnumUtil.sessionKey.USER.getValue());
                    application.removeAttribute(member.getMemberId()+"");
                }else{
                    //如果大于一个,就将当前的session对象从集合里面remove调 同时覆盖ServletContext 存放的相应信息
                    list.remove(session);
                    session.removeAttribute(EnumUtil.sessionKey.USER.getValue());
                    userSessionLoginKeyMap.put(member.getMemberId()+"",list);
                    application.setAttribute(member.getMemberId()+"",userSessionLoginKeyMap);
                }
            }
        }
    }
}
 

备注:
1. 执行session.invalidate()时。 
2. session超时自动销毁时。 
3. 执行session.setAttribute(“anyname”, “其他对象”)或session.removeAttribute(“anyname”)将listener从session中删除时。只要不将listener从session中删除,就可以监听到session的销毁。

二、web.xml文件配置

<listener>
   <listener-class>com.maple.scof.member.common.MemberSessionListener</listener-class>
</listener>
三、登录controller
package com.maple.scof.member.controller;

import java.io.IOException;
import java.util.*;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.maple.scof.business.entity.MemberEntity;
import com.maple.scof.business.entity.UserOperateLog;
import com.maple.scof.business.service.portal.ITaskSMSService;
import com.maple.scof.business.service.portal.IMemberService;
import com.maple.scof.business.util.DataUtil;
import com.maple.scof.member.util.EnumUtil.HttpStatus;
import com.maple.scof.member.util.EnumUtil.sessionKey;
import com.maple.scof.member.util.JsonResult;
import com.maple.scof.member.util.JsonResultObject;
import com.maple.scof.member.util.RequestUtil;
import com.maple.scof.member.util.SessionUtil;
import com.maple.scof.member.util.ValidateCode;

import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
public class CommonController {
 
 
@Autowired
private ServletContext servletContext;

 
 
 
 
/**
 * 会员登录
 * 
 * @param req
 * @param resp
 * @param param
 * @return
 */
@RequestMapping(value = "/fengyeLogin", method = RequestMethod.POST)
@ResponseBody
public JsonResultObject<Object> memberLogin(HttpServletRequest req, HttpServletResponse resp,
      @RequestBody Map<String, String> param) {
   JsonResultObject<Object> resultJson = new JsonResultObject<Object>();
   String phone = param.get("phone");
   String password = param.get("password");
   String code = param.get("code");

   //判断验证码是否正确 
   ******* 西安枫叶 ********

   // 获取登录客户端的IP地址
   String clientIP = RequestUtil.getIpAddr(req);
   UserOperateLog userLog = new UserOperateLog(1, clientIP);
   try {
      Map<String, Object> result = fengeyService.loginMember(phone, password, userLog);
      MemberEntity member = (MemberEntity) result.get("member");
      if(member==null){
        ******* 西安枫叶 *******
      }else if(member.getBlackMemberId()!=0){
         ******* 西安枫叶 *******
      }else if (DataUtil.getString(result.get("flag")).equals("true")) {

         //登录成功将当前用户对象放入session
         HttpSession session = req.getSession();
         session.setAttribute(sessionKey.USER.getValue(), member);
         //同时获取根据用户id上下文的对应的map
         Map<String,List<HttpSession>>  userSessionLoginKeyMap = (HashMap<String, List<HttpSession>>)servletContext.getAttribute(member.getMemberId()+"");
         if(userSessionLoginKeyMap == null){
             userSessionLoginKeyMap = new HashMap<String, List<HttpSession>>();
         }
         List<HttpSession> listCountLogin = userSessionLoginKeyMap.get(member.getMemberId()+"");
         if(listCountLogin == null){
            listCountLogin = new ArrayList<>();
         }
         //添加登录的session到集合
         listCountLogin.add(session);

         //用户如果只关掉了浏览器签页 重新登录那么他的sessionId是一样的
         //需要去掉重复session
         listCountLogin = this.removeStringListDupli(listCountLogin);

         //web.xml配置获取session限制个数
         int loginCount = DataUtil.getInt(req.getSession().getServletContext().getInitParameter("loginCount").toString());

         //如果当前登录用户session集合大于限制个数 取一个session 将它销毁
         if(listCountLogin != null && listCountLogin.size() > loginCount){
            HttpSession oldSession = listCountLogin.get(0);
            oldSession.invalidate(); //使oldSession失效    application.removeAttribute(oldSession.getId()); //将oldSession从application中移除
         }

         //将新的session集合给map同时覆盖servletContext 里的map
         userSessionLoginKeyMap.put(member.getMemberId()+"",listCountLogin);
         servletContext.setAttribute(member.getMemberId()+"",userSessionLoginKeyMap);

         ******** 西安枫叶 ******
      } else if (DataUtil.getString(result.get("flag")).equals("false")) {
         ******** 西安枫叶 ******
      } 
   } catch (Exception e) {
      ******* 西安枫叶 *****
   }
   return resultJson;
}


/*集合去重复*/
private List<HttpSession> removeStringListDupli(List<HttpSession> stringList) {
   Set<HttpSession> set = new LinkedHashSet<HttpSession>();
   set.addAll(stringList);
   stringList.clear();
   stringList.addAll(set);
   return stringList;
}
}

备注:这个蛋疼的需求,耗时一天半,这种实现方式仅适用于流量较小的网站,如果有一百万人同时登陆,将对内存空间的资源占用非常高,到那个时候可以使用redis缓存或者我开始提到的shiro、security,另外感谢我的老大王总提供一些参考支持




  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,可以使用session来实现登录功能。通过session,可以在用户登录后将用户信息保存在服务器端,以便在用户的后续请求中进行验证和识别。 首先,在用户登录成功后,可以将用户信息存储在session中。可以使用以下代获取当前session对象并设置用户名属性: ```java session.setAttribute("username", username); ``` 其中,`username`是用户的用户名。 在其他需要验证用户登录状态的地方,可以通过以下代获取session对象,并检查是否存在用户名属性: ```java HttpSession session = request.getSession(false); if (session == null || session.getAttribute("username") == null) { // 用户未登录,跳转到登录页面 response.sendRedirect("login.jsp"); } else { // 用户已登录,可以使用session对象获取用户信息 String username = (String) session.getAttribute("username"); // 其他操作 } ``` 这段代首先尝试获取当前session对象,如果session不存在或者session中不存在用户名属性,则说明用户未登录,可以将用户重定向到登录页面。如果session存在且用户名属性存在,则说明用户已登录,可以使用session对象获取用户信息。 需要注意的是,为了保证安全性,建议设置session的超时时间。可以在web.xml文件中配置session的超时时间,例如设置为30分钟: ```xml <session-config> <session-timeout>30</session-timeout> </session-config> ``` 另外,为了监听session的创建和销毁事件,可以创建一个实现`HttpSessionListener`接口的类,并在其中重写`sessionCreated`和`sessionDestroyed`方法。在`sessionCreated`方法中,将session对象添加到一个自定义的`SessionBroadcaster`中,在`sessionDestroyed`方法中,将session对象从`SessionBroadcaster`中移除。这样可以在其他应用系统中获取session对象时进行检查和使用。 总结起来,使用session实现登录功能的步骤包括: 1. 在用户登录成功后,将用户信息存储在session中。 2. 在需要验证用户登录状态的地方,获取session对象并检查是否存在用户名属性,进行相应的跳转或操作。 3. 设置session的超时时间以保证安全性。 4. 可选:创建一个实现`HttpSessionListener`接口的类,监听session的创建和销毁事件,并在其中处理相应的逻辑。 希望以上信息对您有所帮助! #### 引用[.reference_title] - *1* *3* [实现登录功能 (会话机制session)](https://blog.csdn.net/molu1991/article/details/124062278)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [[Java实现单点登录session ]](https://blog.csdn.net/sqL520lT/article/details/131181807)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值