最近有个新需求,要实现账户在一个浏览器A登录后,当账户再到第二个浏览器登录的时候,就会把第一个浏览器上的会话销毁,强制去登录。
实现原理
第一次登录
- 1:在拦截器/过滤器中,获取用户输入的地址,如果是登录,就跳转到登录上,然后生成一个session。在这个session中保存一个key loginUser value就是这个用户的信息
- 2:设置一个全局map。map中保存了这个用户的姓名(作为key)以及本次session的ID(作为value),在保存前,先判断这个map里面是否保存了相同的key,如果没有,就保存进去
第二次登录
- 1:当第二次登录的时候,再生成一个session,全局map此时已经存在了同名的key了。根据本次登录的账户名,取出上一次同名的session。然后就简单的从map中移除掉,然后把最新的id添加进去
第一个强制登录
- 1:当用户进行任何操作的时候,会被拦截器拦截,然后根据key
loginUser 获取session中的用户信息,如果为空,可能是session过期了
如果不为空,那么就从session中获取同名的用户信息,假如本次的session ID和从全局map中保存的session ID不一样。就说明。当前的用户所具备的session id 已经被从全局map
中移除掉了,那么就重定向到登录页面上。
代码
- 1:登录
@PostMapping("/login")
public String doLogin(HttpServletRequest request, HttpServletResponse response, Model model){
String userName = request.getParameter("userName");
String password = request.getParameter("password");
System.out.println(userName+" : "+password);
LoginUser user = userService.findUser(userName, password);
model.addAttribute("name",userName);
//使用过滤器来进行校验重复,有可能存在多个账号
HttpSession currentSession = request.getSession();
currentSession.setAttribute("loginUser",user);
Map<String, String> sessionMap = SessionManager.getSessionMap();
if(!sessionMap.containsKey(user.getUserName())){
sessionMap.put(user.getUserName(),currentSession.getId());
System.out.println(user.getUserName()+" 第一次登陆时的session ID是:" + currentSession.getId());
}else{
//同名用户上一次登录的时候
String beforeSessionID = sessionMap.get(user.getUserName());
if(sessionMap.containsKey(user.getUserName()) && !beforeSessionID.equals(currentSession.getId()) ){
sessionMap.remove(user.getUserName());
sessionMap.put(user.getUserName(),currentSession.getId());
}
}
return "hello";
}
- 2: 拦截器
public class MyFilter implements Filter {
private Set<String> excludeUrl = new HashSet<>(Arrays.asList("/login", "/loginOut","/css/**","/fonts/**","/js/**","/favicon.ico"));
private String[] excludesPattern = new String[]{
"/login",
"/favicon.ico",
"/css/**",
"/fonts/**",
"/js/**",
"/loginOut",
"/login*",
"/org.apache*",
};
private PathMatcher pathMatcher=new AntPathMatcher();
public boolean uriIsExist(String uri){
for(String path: excludesPattern){
if(pathMatcher.match(path,uri)){
return true;
}
}
return false;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpSession currentSession = request.getSession();
String requestURI = request.getRequestURI();
if(uriIsExist(requestURI)){
filterChain.doFilter(servletRequest,servletResponse);
return;
}
LoginUser loginUser = (LoginUser) currentSession.getAttribute("loginUser");
//如果user为空,可能是session过期,或者是被强制清空了。必须重新登录
if (loginUser == null) {
response.sendRedirect(request.getServletContext() + "/login");
} else {
Map<String, String> sessionMap = SessionManager.getSessionMap();
//获取session中保存的用户名称。
String userName = loginUser.getUserName();
//获取已经存在的该用户所保存的sessionID.
String beforeSessionID = sessionMap.get(userName);
//如果现在登录成功的同名用户的sessionid和已经存在的id不一致,表示同样名字的session会话在后面产生了,又有一个人登录上来了。
//本次sessionid已经是属于要被强制下线的session了。
/**
* 核心代码是在公用map中,如果存在同名用户,就删除上一次会话,保存新的会话。这个map里面一直保存最新的session ID
*/
if (!currentSession.getId().equals(beforeSessionID)) {
String requestPath= request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath();
response.sendRedirect(requestPath + "/login");
} else {
//表示是最近登录上来的用户的操作。可以放行
filterChain.doFilter(servletRequest, servletResponse);
}
}
}
@Override
public void destroy() {
}
}
网友评论