最近在写项目的时候遇到一个问题:如何实现用户唯一登录?一开始的想法是给t_user表添加一个字段login_status(登录状态),用户登录前去查询t_user的login_status的值,login_status为未登录状态,则可以进行登录;否则,不能登录。但是在用户非正常退出的情况下(即用户未点击“退出”按钮或浏览器非正常关闭),login_status的值一直为登录状态,用户无法进行登录。
解决方案:设置session失效时间,利用session监听,在session销毁的时候去更新login_status的值。
步骤一:在web.xml中添加如下配置:
<!-- session监听 -->
<listener>
<listener-class>cn.tomato.listener.SessionListener</listener-class>
</listener>
<!-- session过期时间设置 -->
<session-config>
<!-- session过期时间为30分钟 -->
<session-timeout>30</session-timeout>
</session-config>
步骤二:创建session监听类
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import cn.tomato.util.SessionFactory;
public class SessionListener implements HttpSessionListener{
public static SessionFactory sessionFactory=SessionFactory.getInstance();
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
System.out.println("====================="+"SessionListener's sessionCreated");
sessionFactory.createSession(httpSessionEvent.getSession());
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
System.out.println("====================="+"SessionListener's sessionDestroyed");
sessionFactory.destroySession(httpSessionEvent.getSession());
}
}
步骤三:创建单例类SessionFactory,管理session的创建及销毁。
import java.util.HashMap;
import javax.servlet.http.HttpSession;
import cn.tomato.domain.User;
import cn.tomato.service.impl.UserServiceImpl;
public class SessionFactory{
private static SessionFactory instance;
private HashMap<String,HttpSession> sessionMap;
private SessionFactory() {
sessionMap = new HashMap<String,HttpSession>();
}
public static SessionFactory getInstance() {
if (instance == null) {
instance = new SessionFactory();
}
return instance;
}
public synchronized void createSession(HttpSession session) {
if (session != null) {
sessionMap.put(session.getId(), session);
System.out.println("CreateSession's sessionId :"+session.getId());
}
}
public synchronized void destroySession(HttpSession session) {
if (session != null) {
if(sessionMap.get(session.getId())!=null){
sessionMap.remove(session.getId());
}else{
User user = (User) session.getAttribute("session_user");
//排除未登录的情况
if(user!=null){
user.setLoginStatus(User.OUTLOGIN);
//普通类调用Service
UserServiceImpl userServiceImpl = (UserServiceImpl)SpringBeanFactory.getBean("userServiceImpl");
userServiceImpl.updateULoginStatus(user);
sessionMap.remove(session.getAttribute(user.getLoginName()),session);
}
}
}
}
public synchronized HttpSession getSession(String sessionId) {
if (sessionId == null) return null;
return (HttpSession) sessionMap.get(sessionId);
}
public synchronized HashMap<String, HttpSession> getSessionMap() {
return sessionMap;
}
public synchronized void setSessionMap(HashMap<String, HttpSession> sessionMap) {
this.sessionMap.putAll(sessionMap);
}
public synchronized void removeSessionMap(String sessionId) {
this.sessionMap.remove(sessionId);
}
}
步骤四:在用户登录及用户退出时更新sessionMap
//登录
@RequestMapping("/login.action")
public String login(User user,HttpServletRequest request,RedirectAttributes redirectAttributes)
{
//判断是否存在此用户
User mUser = userService.findUserByName(user.getLoginName());
if(mUser!=null&&user.getPassWord().equals(mUser.getPassWord())&&mUser.getLoginStatus().equals(User.OUTLOGIN))
{
HttpSession session = request.getSession(false);
mUser.setLoginStatus(User.ONLOGIN);
userService.updateULoginStatus(mUser);
//创建session对象
session.setAttribute("session_user",mUser);
//将服务器ip传给session,后续websocket的url从session中取ip
session.setAttribute("serverIp",contextData.getServerIp());
//移除以sessionId为key的值,用userLoginName代替
sessionFactory.removeSessionMap(session.getId());
HashMap<String,HttpSession> sessionMap = new HashMap<String,HttpSession>();
//sessionMap以userLoginName为key,session为value
sessionMap.put(mUser.getLoginName(),session);
sessionFactory.setSessionMap(sessionMap);
//跳转至首页
System.out.println("------------------------跳转至首页---------------");
return "redirect:/toIndex";
}else{
if(mUser!=null&&!user.getPassWord().equals(mUser.getPassWord())){
redirectAttributes.addFlashAttribute("message", "密码不正确,请核实!");
}else if(mUser!=null&&!mUser.getLoginStatus().equals(User.OUTLOGIN)){
redirectAttributes.addFlashAttribute("message", "用户已经登录,请核实!");
}else if(mUser==null){
redirectAttributes.addFlashAttribute("message", "无此用户,请核实!");
}else{
redirectAttributes.addFlashAttribute("message", "用户名有误,请核实!");
}
return "redirect:/toLogin";
}
}
//退出
@RequestMapping("/toQuit")
public String quit(HttpServletRequest request)
{
HttpSession session = request.getSession(false);
if(session!=null){
User user = (User) session.getAttribute("session_user");
if(user!=null){
HttpSession userSession=(HttpSession)sessionFactory.getSessionMap().get(user.getLoginName());
if(userSession!= null){
//注销在线用户
userSession.invalidate();
sessionFactory.getSessionMap().remove(user.getLoginName());
}
}
}
return "redirect:/toIndex";
}
总结下上面代码的实现思路:首先一开始创建一个新的session时,调用SessionFactory的createSession方法将以sessionId为key,session为value的map加入sessionMap中。当用户登录时,如果存在session,则将该sessionMap中移除以sessionId为key的值,替换成以userLoginName为key,session为value;退出时,先判断sessionMap中是否存在以sessionId为key的值,有说明用户未登录,在sessionMap直接移除sessionId即可;否则,先判断从session中是否可以取出用户信息,是,则更新数据库,并从sessionMap中移除userLoginName为key,session为value的记录。
通常在项目中,会遇到在普通类中调用Service层的方法,去进行数据库操作,总不能将一个普通类加上@Controller的注解吧!就像上面的代码中SessionFactory的destroySession方法中要更新登录状态。解决方法如下:
步骤一:创建一个工厂类SpringBeanFactory
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class SpringBeanFactory implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return context;
}
public static Object getBean(String beanName) {
return context.getBean(beanName);
}
}
步骤二:在Spring的配置文件中配置如下内容
<bean id="springBeanFactory" class="cn.tomato.util.SpringBeanFactory"/>
步骤三:通过SpringBeanFactory的getBean()方法直接获得对应的对象即可。
//普通类调用Service
UserServiceImpl userServiceImpl = (UserServiceImpl)SpringBeanFactory.getBean("userServiceImpl");
最近在用websocket实现单聊,后台数据写好了,前端不会写,有点尴尬。。。。。
关于我
可以扫描关注下面的公众号(公众号:猿类进化论)