天天看點

一個使用者隻能登入一次

僅僅思路,這是springsecurety的

package com.dbapp.fly.config.security;
 
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.dbapp.fly.entity.SysUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

//登入使用者認證通過後,顯示登入成功頁面前,做的操作。
@Component
public class MyAuthenctiationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
 
	private static final Logger logger = LoggerFactory.getLogger(MyAuthenctiationSuccessHandler.class);
 
	// key為sessionId,value為HttpSession,使用static,定義靜态變量,使之程式運作時,一直存在記憶體中。
	// 儲存所有已經登入使用者的會話(每個浏覽器一個會話)
	public static HashMap<String, HttpSession> sessionMap = new HashMap<String, HttpSession>();
 
	@Autowired
	// @Qualifier("sessionRegistry")
	private SessionRegistry sessionRegistry;
 
	// @Bean(name="sessionRegistry",value="sessionRegistry")
	@Bean
	// @Bean(name="sessionRegistry")
	public SessionRegistry getSessionRegistry() {
		return new SessionRegistryImpl();
	}
 
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
			Authentication authentication) throws ServletException, IOException {
 
		// 1.登入認證成功後,擷取使用者名
		//(隻能在認證成功通過後,才能獲得sc,不然在CustomUserService implements UserDetailsService的loadUserByUsername方法中是第二次才能擷取到)
		SecurityContext sc = SecurityContextHolder.getContext();
		String currentuser = ((SysUser) sc.getAuthentication().getPrincipal()).getUserName();
		logger.info("目前登入使用者:" + currentuser);
 
		
		// 2.先判斷使用者是否重複登入
		Iterator<Entry<String, HttpSession>> iterator = sessionMap.entrySet().iterator();
		while(iterator.hasNext()) {
			Map.Entry<String, HttpSession> entry = iterator.next();
			HttpSession session = entry.getValue();
			// 2.1 判斷session中所包含的使用者名稱是否有目前登入使用者
			String username = SessionUtil.getUserName(session);
			if (currentuser.equals(username)) {
				logger.info("使用者:" + currentuser + "已經在其它地方登入過,将踢除!");
				SessionUtil.expireSession(session);
				logger.info("删除的會話:"+entry.getKey());
				// 2.2 從sessionMap中踢除會話
				iterator.remove();
				// 2.3 從sessionRegistry中踢除會話
				sessionRegistry.removeSessionInformation(session.getId());
			}
		}
		
		/*//以下這種方法會引起java.util.ConcurrentModificationException: null 錯誤,    HashMap
		// 2.先判斷使用者是否重複登入
		for (Entry<String, HttpSession> entry : sessionMap.entrySet()) {
			HttpSession session = entry.getValue();
			// 2.1 判斷session中所包含的使用者名稱是否有目前登入使用者
			String username = SessionUtil.getUserName(session);
			if (currentuser.equals(username)) {
				logger.info("使用者:" + currentuser + "已經在其它地方登入過,将踢除!");
				SessionUtil.expireSession(session);
				logger.info(entry.getKey());
				sessionMap.remove(entry.getKey());//這裡會引起同步錯誤
				sessionRegistry.removeSessionInformation(session.getId());
			}
		}*/
 
		// 3.将目前session儲存到sessionMap中
		logger.info("将目前會話:" + request.getSession().getId() + ",儲存到sessionMap");
		sessionMap.put(request.getSession().getId(), request.getSession());
		for (Entry<String, HttpSession> entry : sessionMap.entrySet()) {
			logger.info("顯示已經儲存的sessionMap:Key: " + entry.getKey() + " Value: " + entry.getValue());
		}
 
		// 4.列印所有認證通過的使用者(包含重複登入的,不過上面已經踢除了)
		List<Object> principals = sessionRegistry.getAllPrincipals();
  		List<String> usersNamesList = new ArrayList<String>();
  		for (Object principal: principals) {
  		    if (principal instanceof SysUser) {
  		        usersNamesList.add(((SysUser) principal).getUserName());
  		    }
  		}
  		logger.info("已經認證通過的使用者數:"+usersNamesList.size()+",     已經認證通過使用者:"+usersNamesList.toString());
		
		
		// response.sendRedirect("/");
		super.onAuthenticationSuccess(request, response, authentication);
	}
 
}
           
package com.dbapp.fly.config.security;

import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

//啟動類加上注解@ServletComponentScan,這樣才能掃描到監聽器
//@WebListener
public class MySessionListner implements HttpSessionListener {

	private static final Logger logger = LoggerFactory.getLogger(MySessionListner.class);

	/**
	 * 建立session時(打開浏覽器通路登入頁面時,伺服器會建立一個新的session)
	 */
	@Override
	public void sessionCreated(HttpSessionEvent httpSessionEvent) {
		System.out.println("建立session");

	}

	/**
	 * 删除session時(退出系統)
	 */
	@Override
	public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
		logger.info("銷毀session時");
		MyAuthenctiationSuccessHandler.sessionMap.remove(httpSessionEvent.getSession().getId());
	}

}
           
package com.dbapp.fly.config.security;
 
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
@Configuration
public class MywebConfig implements WebMvcConfigurer {
    @Bean
    public ServletListenerRegistrationBean listenerRegist() {
        ServletListenerRegistrationBean srb = new ServletListenerRegistrationBean();
        srb.setListener(new MySessionListner());
        System.out.println("listener");
        return srb;
    }
}
           
package com.dbapp.fly.config.security;
 
import java.util.Enumeration;
import java.util.List;
 
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import com.dbapp.fly.entity.SysUser;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.User;

 
public class SessionUtil {
 
	private static SecurityContext attribute;
	
	/**
	 * 根據目前session擷取目前登入使用者對象
	 * @param session
	 * @return guser
	 */
	public static SysUser getUser(HttpSession session) {
		try {
			attribute = (SecurityContext) session.getAttribute("SPRING_SECURITY_CONTEXT");
			SysUser principal = (SysUser) attribute.getAuthentication().getPrincipal();
			return principal;
		} catch (Exception e) {
		}
		return null;
	}
	
	/**
	 * 根據目前session擷取目前登入使用者ID
	 * @param session
	 * @return guser
	 */
	public static Long getUserId(HttpSession session) {
		try {
			attribute = (SecurityContext) session.getAttribute("SPRING_SECURITY_CONTEXT");
			SysUser principal = (SysUser) attribute.getAuthentication().getPrincipal();
			return principal.getId();
		} catch (Exception e) {
		}
		return null;
	}
	
	/**
	 * 根據session擷取使用者名稱
	 * @param session
	 * @return void
	 */
	public static String getUserName(HttpSession session) {
		try {
			attribute = (SecurityContext) session.getAttribute("SPRING_SECURITY_CONTEXT");
			SysUser principal = (SysUser) attribute.getAuthentication().getPrincipal();
			return principal.getUserName();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
}
	
	/**
	 * 根據session擷取count
	 * session中包含一個count鍵預設為null,可以用來統計登入次數
	 * @param session
	 * @return void
	 */
	public static void count(HttpSession session) {
		ServletContext context = session.getServletContext();
		
		System.out.println("sessionid:"+session.getId()+",的count是:"+context.getAttribute("count"));
	}
	
	
    /**
     * 辨識使用者是否已經登入,如果已經登入就不能登入了。
     *
     * @param request
     * @param sessionRegistry
     * @param loginedUser
     */
    public static void deleteSameUser(HttpServletRequest request, SessionRegistry sessionRegistry, User loginedUser) {
        SecurityContext sc = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
        List<SessionInformation> sessionsInfo;
        sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), true);
        String currentSessionId;
        if (null != sessionsInfo && sessionsInfo.size() == 0) {
            sessionRegistry.registerNewSession(request.getSession().getId(), sc.getAuthentication().getPrincipal());
            sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), false);
        }
        currentSessionId = sessionsInfo.get(0).getSessionId();
        List<Object> o = sessionRegistry.getAllPrincipals();
        for (Object principal : o) {
            if (principal instanceof User && (loginedUser.getUsername().equals(((User) principal).getUsername()))) {
                List<SessionInformation> oldSessionsInfo = sessionRegistry.getAllSessions(principal, false);
                if (null != oldSessionsInfo && oldSessionsInfo.size() > 0 && !oldSessionsInfo.get(0).getSessionId().equals(currentSessionId)) {
                    for (SessionInformation sessionInformation : sessionsInfo) {
                        //目前session失效
                        sessionInformation.expireNow();
                        sc.setAuthentication(null);
                        sessionRegistry.removeSessionInformation(currentSessionId);
                        //throw new GeneralServerExistException(ErrorMessage.ALONG_LOGIN_ERROTR.toString());
                    }
                }
            }
        }
    }
 
    /**
     * 會話銷毀(剔除前一個使用者)
     *
     * @param , SysMessageService sysMessageService
     */
    public static void expireSession(HttpSession session) {
    	session.invalidate();
    }
    
    
    /**
     * 剔除前一個使用者
     *
     * @param request
     * @param sessionRegistry
     * @param loginedUser
     * @param , SysMessageService sysMessageService
     */
    public static void dropPreviousUser2(HttpServletRequest request, SessionRegistry sessionRegistry, SysUser loginedUser) {
    	List<SessionInformation> sessionsInfo = null;
    	//登入以後session裡才會加入鍵名為"SPRING_SECURITY_CONTEXT"的字段
        SecurityContext sc = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
        
        if(sc!=null) {
        	System.out.println("!!!!!!!!!!!!"+sc.getAuthentication().getPrincipal().toString());
        	//擷取目前登入使用者的會話資訊集合
        	sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), false);
        	if (sessionsInfo.size() > 0) {
        		//目前會話ID
        		String  currentSessionId = sessionsInfo.get(0).getSessionId();
        		//擷取所有已經登入的使用者
        		List<Object> o = sessionRegistry.getAllPrincipals();
        		for (Object principal : o) {
        			//當登入使用者的名字和已經登入使用者的名字相同,也就是登入使用者已經登入過了。
        			if (principal instanceof User && (loginedUser.getUserName().equals(((User) principal).getUsername()))) {
        				//擷取已經登入使用者的會話資訊集合
        				List<SessionInformation> oldSessionsInfo = sessionRegistry.getAllSessions(principal, false);
        				//如果會話資訊不為空且會話資訊的ID不等于目前會話ID
        				if (null != oldSessionsInfo && oldSessionsInfo.size() > 0 && !oldSessionsInfo.get(0).getSessionId().equals(currentSessionId)) {
        					//周遊已經登入使用者的會話資訊,并設定過期,即删除session
        					for (SessionInformation sessionInformation : oldSessionsInfo) {
        						//舊使用者的session失效
        						//send message
        						//sysMessageService.sendMessage(((User) principal).getUsername(), new SysMessage(null, Consts.NOTIFICATION_TYPE_HADLOGIN_CONTENT, 5, Consts.NOTIFICATION_ACCEPT_TYPE_HADLOGIN));
        						sessionInformation.expireNow();
        					}
        				}
        			}
        		}
        	}
        }
        
    }
 
    /**
     * session 失效
     *
     * @param request
     * @param sessionRegistry
     */
    public static void expireSession(HttpServletRequest request, User user, SessionRegistry sessionRegistry) {
        List<SessionInformation> sessionsInfo = null;
        if (null != user) {
            List<Object> o = sessionRegistry.getAllPrincipals();
            for (Object principal : o) {
                if (principal instanceof User && (user.getUsername().equals(((User) principal).getUsername()))) {
                    sessionsInfo = sessionRegistry.getAllSessions(principal, false);
                }
            }
        } else if (null != request) {
            SecurityContext sc = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
            if (null != sc.getAuthentication().getPrincipal()) {
                sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), false);
                sc.setAuthentication(null);
            }
        }
        if (null != sessionsInfo && sessionsInfo.size() > 0) {
            for (SessionInformation sessionInformation : sessionsInfo) {
                //目前session失效
                sessionInformation.expireNow();
                sessionRegistry.removeSessionInformation(sessionInformation.getSessionId());
            }
        }
    }
    
    public void showsession(HttpServletRequest request) {
    	//擷取session  
    	HttpSession   session   =   request.getSession();    
    	// 擷取session中所有的鍵值  
    	Enumeration<String> attrs = session.getAttributeNames();  
    	// 周遊attrs中的
    	while(attrs.hasMoreElements()){
    	// 擷取session鍵值  
    	    String name = attrs.nextElement().toString();
    	    // 根據鍵值取session中的值  
    	    Object vakue = session.getAttribute(name);
    	    // 列印結果 
    	    System.out.println("--sessionID"+session.getId());
    	    System.out.println("--名字:" + name +"-----\n");
    	    System.out.println("--值:" + vakue +"--------\n");
    	}
    	
    }
}