一个账号禁止多人使用,如果前一个人登陆了账号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;
}
}