用session監聽實現唯一登入及普通類呼叫Service層的方法的實現思路
阿新 • • 發佈:2019-01-11
最近在寫專案的時候遇到一個問題:如何實現使用者唯一登入?一開始的想法是給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實現單聊,後臺資料寫好了,前端不會寫,有點尷尬。。。。。