一个账号禁止多人使用,如果前一个人登陆了账号A,那么另一个人用账号A登陆将会把前一个人挤掉下线。废话多说,上菜
1.定义一个全局线程安全变量 存放用户最后登陆时间
2.用户登陆时,将用户登陆时间loginTime存在map中(可以用ConcurrentHashMap)key为用户id,value为用户最后登陆时间。
3.登陆过程中,获取session,在session中存放用户登陆时间loginTime.
package cn.quantgroup.cashloanflowboss.api.login.service; import cn.quantgroup.cashloanflowboss.api.login.model.Principal; import cn.quantgroup.cashloanflowboss.api.user.dictionary.UserStatus; import cn.quantgroup.cashloanflowboss.api.user.entity.User; import cn.quantgroup.cashloanflowboss.api.user.model.UserInfo; import cn.quantgroup.cashloanflowboss.api.user.service.UserServiceImpl; import cn.quantgroup.cashloanflowboss.core.asserts.Assert; import cn.quantgroup.cashloanflowboss.core.dictionary.ApplicationDictionary; import cn.quantgroup.cashloanflowboss.core.dictionary.ApplicationStatus; import cn.quantgroup.cashloanflowboss.utils.JSONTools; import cn.quantgroup.cashloanflowboss.utils.MD5Tools; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Created by WeiWei on 2019/7/22. */ @Slf4j @Service public class LoginServiceImpl implements LoginService { @Autowired private UserServiceImpl userService; @Autowired private HttpServletRequest request; /** * 用户为key,登陆信息(最后登陆时间) */ private ConcurrentHashMap<String, Object> loginInfo = new ConcurrentHashMap<>(8); /** * 登入 * * @param username 用户名 * @param password 密码(明文) * @return */ @Override public String login(String username, String password) { User user = this.userService.getUser(username); // 检查用户是否有效 Assert.isNull(user, ApplicationStatus.INVALID_USER); // 检查用户是否被禁用 Assert.isTrue(UserStatus.DISABLED.equals(user.getStatus()), ApplicationStatus.DISABLED_USER); // 检查密码是否正确 Assert.isFalse(user.getPassword().equalsIgnoreCase(MD5Tools.md5(password)), ApplicationStatus.USERNAME_OR_PASSWORD_ERROR); // 创建Session HttpSession session = this.request.getSession(true); // 设置用户主要信息 Principal principal = new Principal(); UserInfo userInfo = new UserInfo(); userInfo.setUserId(user.getId()); userInfo.setUsername(user.getUsername()); userInfo.setNickname(user.getNickname()); principal.setUserInfo(userInfo); principal.setChannelId(user.getChannelId()); principal.setRank(user.getRank()); principal.setRoles(user.getRoles()); long currentTimeMillis = System.currentTimeMillis(); session.setAttribute(ApplicationDictionary.PRINCIPAL, JSONTools.serialize(principal)); session.setAttribute(ApplicationDictionary.USER_SESSION_LOGIN_TIME, currentTimeMillis); // session登陆时间,毫秒值 // 用户登陆时间,毫秒值 loginInfo.put(userInfo.getUserId() + "", currentTimeMillis); // 保存用户最后登陆时间 user.setLastLoginTime(new Date()); userService.updateUser(user); return session.getId(); } /** * 登出 * * @return */ @Override public boolean logout() { this.request.getSession().removeAttribute(ApplicationDictionary.PRINCIPAL); return true; } @Override public Map<String, Object> getConcurrentHashMapLoginInfo() { return loginInfo; } }
4.每次访问,如果session.loginTime < map.loginTime 那么这个session就是应该被挤掉的session,将session退出,退出登陆。
package cn.quantgroup.cashloanflowboss.core.configuration; import cn.quantgroup.cashloanflowboss.api.login.model.Principal; import cn.quantgroup.cashloanflowboss.api.login.service.LoginService; import cn.quantgroup.cashloanflowboss.api.login.service.LoginServiceImpl; import cn.quantgroup.cashloanflowboss.api.role.entity.Role; import cn.quantgroup.cashloanflowboss.component.security.Authority; import cn.quantgroup.cashloanflowboss.component.security.SecurityHandler; import cn.quantgroup.cashloanflowboss.core.Application; import cn.quantgroup.cashloanflowboss.core.asserts.Assert; import cn.quantgroup.cashloanflowboss.core.dictionary.ApplicationStatus; import cn.quantgroup.cashloanflowboss.core.exception.ApplicationException; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; /** * Created by WeiWei on 2019/7/26. */ @Configuration public class ApplicationSecurityHandler implements SecurityHandler { @Autowired private LoginService loginService; @Override public boolean doAuthentication(MethodInvocation invocation, String authorityId, Authority[] authority) throws Throwable { Principal principal = Application.getPrincipal(); // 检查是否已登录 Assert.isNull(principal, ApplicationStatus.AUTHENTICATION_LOGIN); // 是否 被挤下线 Boolean isLogin = isLastLogin(loginService.getConcurrentHashMapLoginInfo()); if (isLogin != null && isLogin) { // 退出登陆 loginService.logout(); // 返回 信息 throw new ApplicationException(ApplicationStatus.AUTHENTICATION_LOGIN_CROWD_OUT); } // 如果是超级管理员跳过权限验证 return principal.isSuperAdministrator() || principal.getRoles().stream().anyMatch(role -> { List<Role> roleList = getRoleAndParent(role); if (CollectionUtils.isEmpty(roleList)) { return false; } return roleList.stream().anyMatch(_role -> this.checkAuthority(authorityId, _role)); }); } /** * 遍历出用户的父级 role * @param role * @return */ private List<Role> getRoleAndParent(Role role) { if (role == null) { return null; } List<Role> list = new ArrayList<>(); list.add(role); while (role.getParent() != null) { role = role.getParent(); list.add(role); } return list; } /** * 检查权限 * * @param authorityId * @param role * @return */ private boolean checkAuthority(String authorityId, Role role) { if (role == null) { return false; } return CollectionUtils.isNotEmpty(role.getPermissions()) && role.getPermissions().parallelStream().anyMatch(permission -> permission.getName().equals(authorityId)); } /** * @param concurrentHashMapLoginInfo 账号最后登陆时间 * */ private Boolean isLastLogin(Map<String, Object> concurrentHashMapLoginInfo) { HttpSession session = Application.getSession(); if (Objects.isNull(session)) { System.out.println("session 为空"); return false; } Principal principal = Application.getPrincipal(); if (Objects.isNull(principal)) { System.out.println("principal 为空"); return false; } Object userLastLoginTimeObject = concurrentHashMapLoginInfo.get(principal.getUserInfo().getUserId()+""); if (Objects.isNull(userLastLoginTimeObject)) { // 没有登陆时间 不处理 System.out.println("userLastLoginTimeObject 为空"); return false; } // 获取session最后登陆时间 Object creationTimeObject = session.getAttribute(ApplicationDictionary.USER_SESSION_LOGIN_TIME); if (Objects.isNull(creationTimeObject)) { System.out.println("lastAccessedTime 为空"); return false; } try { if (Long.valueOf(creationTimeObject.toString()) < Long.valueOf(userLastLoginTimeObject.toString())) { return true; } } catch (NumberFormatException e) { return false; } return false; } }