spring boot security 防止用户重复登录(原创)

原理:在认证成功通过后,在显示登录成功页面之前,也就是在SavedRequestAwareAuthenticationSuccessHandler类中操作。

添加一个集合sessionMap 用于保存认证成功的会话,键名为会话ID,

每次有用户登录认证通过都要判断一下是否重复登录,如果不是继续执行,将会话保存在集合sessionMap里。

如果是就踢除之前登录过的用户的会话,将旧的会话从集合sessionMap里删除,还有一步就是将旧的会话从sessionRegistry里删除(不然你如果要获取所有认证通过的用户,还会从sessionRegistry中获取旧用户),然后将自己的新会话保存到集合sessionMap里。(每次认证通过的用户会话都会保存到sessionRegistry里,所以不必自己再写一遍)

package com.mayocase.handler;

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 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;

import com.mayocase.domain.SysUser;
import com.mayocase.util.SessionUtil;

//登录用户认证通过后,显示登录成功页面前,做的操作。
@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.mayocase.util;

import java.util.Enumeration;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

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;

import com.mayocase.domain.SysUser;

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 Integer 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 request
     * @param sessionRegistry
     * @param loginedUser
     * @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");
    	}
    	
    }
}

 SessionRegistry引入的方法

package com.mayocase.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@ImportResource(locations={"classpath:spring-security-context.xml"})
public class XMLConfigClass {

}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:security="http://www.springframework.org/schema/security"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
						   http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

	

    
    
    <bean id="mySessionAuthenticationStrategy" 
           class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
       <constructor-arg  name="sessionRegistry"  ref="sessionRegistry"/>
       <property name="maximumSessions" value="1" /> 
    </bean>
    <bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
    
    
</beans>

添加一个监听器,用于控制session过期的情况,

package com.mayocase.listener;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;

import com.mayocase.domain.SysUser;
import com.mayocase.handler.MyAuthenctiationSuccessHandler;

//启动类加上注解@ServletComponentScan,这样才能扫描到监听器
@WebListener
public class MySessionListner    implements HttpSessionListener {

	private static final Logger logger = LoggerFactory.getLogger(MySessionListner.class);
	
	/**
	 * 新建session时(打开浏览器访问登录页面时,服务器会创建一个新的session)
	 */
	@Override
	public void sessionCreated(HttpSessionEvent httpSessionEvent) {
		
		
	}
	/**
	 * 删除session时(退出系统)
	 */
	@Override
	public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
		logger.info("销毁session时");
		MyAuthenctiationSuccessHandler.sessionMap.remove(httpSessionEvent.getSession().getId());
	}
	
}

猜你喜欢

转载自blog.csdn.net/zhuyongru/article/details/82622647