Listener单态登陆

        单态登陆,或者成为单一登陆,就是一个账号只能在一台机器上登录,如果在其他机器上登陆了,则原来的登陆自动失效。单态登陆的目的是为了防止多台机器同时使用一个账号。

        本例使用一个简单的JSP页面来模拟登陆情况。如果session中有userInfo信息,则表示已经登陆,页面将显示登陆后的账号。如果session中没有userInfo信息,则表示没有登录,页面将显示登陆输入框。登陆与注销动作都在在JSP中完成,代码如下,

<%@ page import="model.UserInfo" %>
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>
<head>
    <title>Title</title>
</head>
<body>

<%
    String action = request.getParameter("action");
    String account = request.getParameter("account");
    UserInfo userInfo = (UserInfo)session.getAttribute("userInfo");
    if("login".equals(action)){
        //登陆,将userInfo放入session
        userInfo = new UserInfo();
        userInfo.setAccount(account);
        userInfo.setIp(request.getRemoteAddr());
        userInfo.setLoginDate(new Date());
        session.setAttribute("userInfo",userInfo);
        response.sendRedirect(response.encodeRedirectURL(request.getRequestURI()));
    }else if("logout".equals(action)){
        //注销,将userInfo从session中移除
        session.removeAttribute("userInfo");
        response.sendRedirect(response.encodeRedirectURL(request.getRequestURI()));
    }

%>

<%
    if(userInfo!=null)
    {
        %>
            欢迎您:<%=userInfo.getAccount()%><br>
            您的登陆IP为:<%=userInfo.getIp()%><br>
            登陆时间为:<%=userInfo.getLoginDate()%><br>
            <a href="<%=request.getRequestURI()%>?action=logout">退出</a>
            <script>setTimeout("location=location;",5000)</script>
        <%
    }
    else
    {
        %>
            <p style="color: red"><%=session.getAttribute("msg")%></p>
            <form action="<%=request.getRequestURI()%>?action=login" method="post">
                账号<input type="text" name="account" >
                <input type="submit" value="登陆">
            </form>
        <%
    }
%>

</body>
</html>


        为了保证信息的 实时性,登陆后的代码中添加了一句JavaScript代码,每隔5秒刷新一次页面。这样,如果账号在别处登陆,5秒之内就会显示被迫下线的信息。userInfo为简单的JavaBean,用于记录登录者信息,代码如下

package model;

import java.io.Serializable;
import java.util.Date;

public class UserInfo implements Serializable {

    private String account;
    private String ip;
    private Date loginDate;

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public Date getLoginDate() {
        return loginDate;
    }

    public void setLoginDate(Date loginDate) {
        this.loginDate = loginDate;
    }
}


        单态登陆的代码写在Listener中,通过监听Session和userInfo属性的添加与删除实现。如果新添加了一个userInfo属性,则认为是新登录用户,Listener中查看该用户的账号是否在其他机器上登录过。如果已经登陆,则让旧的登陆信息失效。Listener中使用一个Map把所有包含userInfo信息的Session收集起来。代码如下

package listener;

import model.UserInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import java.util.HashMap;
import java.util.Map;

@WebListener()
public class LoginSessionListener implements  HttpSessionAttributeListener {

    Log log = LogFactory.getLog(this.getClass());

    //用于保存全部session的map
    Map<String,HttpSession> map = new HashMap<>();
    @Override
    public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
        String name = httpSessionBindingEvent.getName();
        //登陆
        if(name.equals("userInfo")){
            UserInfo userInfo = (UserInfo)httpSessionBindingEvent.getValue();
            if(map.get(userInfo.getAccount())!=null){
                //map中有记录,表明在其他机器中登陆过,将以前的登陆失效
                HttpSession session = map.get(userInfo.getAccount());
                UserInfo oldInfo = (UserInfo)session.getAttribute("userInfo");
                log.info("账号:"+oldInfo.getAccount()+"在"+oldInfo.getIp()+"已经登陆,该登陆将被迫下线");
                session.removeAttribute("userInfo");
                session.setAttribute("msg","您的账号在其他机器上登录,您被迫下线");
            }
            //将Session以用户名为索引,放入map中
            map.put(userInfo.getAccount(),httpSessionBindingEvent.getSession());
            log.info("账号:"+userInfo.getAccount()+"在"+userInfo.getIp()+"登陆");
        }
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
        String name = httpSessionBindingEvent.getName();
        //注销
        if(name.equals("userInfo")){
            //将该session从map移除
            UserInfo userInfo = (UserInfo)httpSessionBindingEvent.getValue();
            map.remove(userInfo.getAccount());
            log.info("账号:"+userInfo.getAccount()+"注销");
        }
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
        String name = httpSessionBindingEvent.getName();
        //没有注销的情况下用另一个账号登陆
        if(name.equals("userInfo")){
            //移除旧的登陆信息
            UserInfo oldInfo = (UserInfo)httpSessionBindingEvent.getValue();
            map.remove(oldInfo.getAccount());
            //新的登陆信息
            UserInfo userInfo = (UserInfo)httpSessionBindingEvent.getSession().getAttribute("userInfo");
            //也要检查新登录的账号是否在别的机器上登陆过
            if(map.get(userInfo.getAccount())!=null){
                //map中有记录,表明在其他机器中登陆过,将以前的登陆失效
                HttpSession session = map.get(userInfo.getAccount());
                session.removeAttribute("userInfo");
                session.setAttribute("msg","您的账号在其他机器上登录,您被迫下线");
            }

            //将Session以用户名为索引,放入map中
            map.put(userInfo.getAccount(),httpSessionBindingEvent.getSession());
            log.info("账号:"+userInfo.getAccount()+"在"+userInfo.getIp()+"登陆");

        }
    }
}

        在web.xml中配置Listener,代码如下

<listener>
    <listener-class>listener.LoginSessionListener</listener-class>
</listener>

        最后一张为账号在其他浏览器登陆后的效果。

                  






  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值