一、业务需求
账号已在web端登录后,再次登录相同账号将踢掉前一次的登录信息。
二、具体实现
实现思路
1.通过定义一个全局map变量sessionMap来保存用户登录成功时session对象,key为userId,value为HttpSession对象。
2.用户登录成功时将用户信息及session存入sessionMap(同一浏览器session相同)。
3.定义一个全局拦截器Interceptor,拿当前session对象与通过userId获取的HttpSession对象去对比,如果不一致则异地登录,此时给出提示并且跳转到登录页面。
4.(页面操作)当用户在第一次登录页面执行操作时,拦截器进行拦截,由于session不同拦截,而用户在第二次登录页面由于最新sessionMap的session存的就是这个,不会拦截。
5.over。
具体代码
- 登录控制器UsersController
@Controller
public class UsersController {
@Autowired
private UsersService usersServiceImpl;
//key为用户id,value为用户登录时session(同一浏览器session相同)
public static HashMap<String,HttpSession> sessionMap = new HashMap<>();
@RequestMapping("/usersLogin")
public String login(Users users,HttpSession session,HttpServletRequest req) {
Users user = usersServiceImpl.selByUsers(users);
if(user != null) {
//登录成功
session.setAttribute("user",user);
// 添加本次登录用户的sessionMap,当前userId已有记录则覆盖
String userId = String.valueOf(user.getId());
sessionMap.put(userId, session);
if(user.getRid() == UsersService.ADMIN_ID) {
//后台首页
return "redirect:toAdminIndex";
}else if(user.getRid() == UsersService.USER_ID) {
//前端首页
return "redirect:toIndex";
}
}
return "redirect:toLogin";
}
}
2.springmvc配置拦截器
<!-- Interceptor拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/toLogin"/>
<bean class="com.novel.interceptor.MainInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
3.拦截器实现类MainInterceptor
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.novel.controller.UsersController;
import com.novel.pojo.Users;
public class MainInterceptor implements HandlerInterceptor {
/**
* 预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller
* 返回值:true表示继续流程(如调用下一个拦截器或处理器);
* false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//设置request,response字符集,否则输出的script会乱码
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
Users user = (Users) request.getSession().getAttribute("user");
if(user!=null) {
String userId = String.valueOf(user.getId());
HttpSession httpSession = (HttpSession) UsersController.sessionMap.get(userId);
//异地登录
if(!httpSession.getId().equals(request.getSession().getId())){
//跳转至登录页面
response.getWriter().println(
"<script type='text/javascript'>"
+ "alert('您的账号已在异地登录,请重新登录!'); "
+ "top.location.href='"
+ request.getContextPath()+ "/toLogin" + "';</script>");
return false;
}
}else {
//跳转至登录页面
response.getWriter().println(
"<script type='text/javascript'>"
+ "alert('尚未登录或者登录超时,请重新登录!!'); "
+ "top.location.href='"
+ request.getContextPath()+ "/loginPage" + "';</script>");
return false;
}
return true;
}
/**
* 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)
* 对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
}
/**
* 整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,
* 还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
}
}
4.正常注销账号时remove当前用户sessionMap
@RequestMapping("/toLogin")
public String login(HttpServletRequest req) throws Exception {
Users user = (Users) req.getSession().getAttribute("user");
if(user!=null) {
UsersController.sessionMap.remove(String.valueOf(user.getId()));
req.getSession().removeAttribute("user");
}
return "login";
}
@RequestMapping("/loginPage")
public String loginPage(HttpServletRequest req) throws Exception {
return "login";
}