首先我们目的是实现一个单点登录,即只允许单个账户单个登录。
实现异地与同地登录判断
要将先登录的用户强制下线。不免用到session。
单点登录的最大难题:已经登录且重复登录的用户的session怎么web全局操作它
我们来解决它。
基本目的实现的方法之一是{
1.建立一个可以全局存储当前登录人员信息的Map;推荐hashmap,因为更快。
2.建立一个登录人员监听,}
怎么建立全局呢,还记得jsp的内置对象application吗,对,就是用它。
使用session的getservetContext()可以得到
它是存在于整个web项目的,只有项目关闭时才会实现,做全局很合适
具体代码在最后
存储哪些属性呢:
1.每个用户的账户(最好必须是唯一标识),当做Map的key;
2.每个用户产生的session的id,好吧,可以直接存session对象的,我试过,可以当做Map的value。
那么存session对象的好处是什么?你不用拿着session的id去找回session(这步超级麻烦,然而好像网上依旧有许多这样的教程)
就可以直接对已经登录的重复的session进行操作,过程仅仅需要一个强制转化, 直接解决了单点登录的最大问题:已经登录且重复登录的用户的session直接可操作
那么又来了,直接存session没有缺点吗,不可能,你想一个session有可能存着许多的变量,那么这个对象就会变得比较大,如果项目上了级别,耗费资源也是要考虑的
好现在是具体的代码,两部分
1
这是在登录验证的时候的代码,取其精华!强制转换,重复的session获取
即在登录时检查重复登录,当前在线人数及其会话,加入到全局Map。
private void query(String sql) {
try {
Connection co = Jdbc_DBCP_xiu.getConnection();
PreparedStatement p = co.prepareStatement(sql);
ResultSet result = p.executeQuery();
// 设置登录结果
if (result.next()) {
ActionContext.getContext().getSession().put("user", u);
this.loginflag = true;
// 获取session
HttpSession session = ServletActionContext.getRequest().getSession(true);
// 检查重复登录key_user value_session
ServletContext application = session.getServletContext();
@SuppressWarnings("unchecked")
Map<String, HttpSession> login_map_session = (Map<String, HttpSession>) application
.getAttribute("login_map_session");
// hashmap查找更快
if (login_map_session == null) {
login_map_session = new HashMap<>();
}
for (String key : login_map_session.keySet()) {
// 同时在线
if (u.equals(key)) {
// 可尝试,发送信息给未下线的另一用户或强制下线另一用户
HttpSession otherSession=((HttpSession) (login_map_session.get(key)));// 另一用户(HttpSession) login_map_session.get(key)
//登录失败
this.loginflag = false;
if (session.getId().equals(otherSession.getId())) {
session.setAttribute("tiplogin", "该用户同一地方多次登录!其已被强制下线");
//强制本地(同一浏览器)所有重复会话直接下线(一个不留)
//这三句相当于删除了session
login_map_session.remove(session.getAttribute("user"));
session.removeAttribute("user");
session.getServletContext().setAttribute("login_map_session", login_map_session);
//实际不下线,留下谁都行
} else {
session.setAttribute("tiplogin", "该用户已经在其他地方登录!其已被强制下线");
//强制异地(不一浏览器)重复会话直接下线
otherSession.invalidate();
}
return;
}
}
// 当前在线人数及其会话,加入到全局
login_map_session.put(u, session);
application.setAttribute("login_map_session", login_map_session);
// 当前预备下线
} else {
this.loginflag = false;
}
Jdbc_DBCP_xiu.release(co, p, result);
} catch (Exception e1) {
// TODO 自动生成的 catch 块
this.loginflag = false;
}
}
2.session的监听器,实现SessionListener,session登录失效去全局Map中删除
package servlet.listener;
import java.util.Map;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
@WebListener()
public class LoginListener implements javax.servlet.http.HttpSessionListener {
/**
* @author joker
*
*
* 登出,map的session删除
*/
public void sessionCreated(HttpSessionEvent se) {
// TODO 自动生成的方法存根
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
// TODO 自动生成的方法存根
HttpSession s = se.getSession();
// 保存到整个web中
//登出
if (s.getAttribute("user") != null) {
@SuppressWarnings("unchecked")
Map<String, HttpSession> login_map_session = (Map<String, HttpSession>) s.getServletContext().getAttribute("login_map_session");
login_map_session.remove(s.getAttribute("user"));
s.getServletContext().setAttribute("login_map_session", login_map_session);
}
}
}
再加一点自己的前端,爽歪歪