一个账号某一时刻只能在一处登陆

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值