限制统一账号不同终端登录

master
hansha 2 years ago
parent 556fd410c8
commit dc04eea7c6

@ -90,6 +90,8 @@ spring:
# token配置
token:
#是否允许账户多终端同时登录true允许 false不允许
soloLogin: true
# 令牌自定义标识
header: Authorization
# 令牌密钥

@ -1,8 +1,9 @@
package com.da.common.constant;
import java.util.Locale;
import io.jsonwebtoken.Claims;
import java.util.Locale;
/**
*
*
@ -10,6 +11,11 @@ import io.jsonwebtoken.Claims;
*/
public class Constants
{
/**
* redis key
*/
public static final String LOGIN_USERID_KEY = "login_userid:";
/**
* UTF-8
*/

@ -1,23 +1,23 @@
package com.da.framework.security.handle;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import com.alibaba.fastjson2.JSON;
import com.da.common.constant.Constants;
import com.da.common.core.domain.AjaxResult;
import com.da.common.core.domain.model.LoginUser;
import com.da.common.utils.MessageUtils;
import com.da.common.utils.ServletUtils;
import com.da.common.utils.StringUtils;
import com.da.framework.manager.AsyncManager;
import com.da.framework.manager.factory.AsyncFactory;
import com.da.framework.web.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 退
@ -36,7 +36,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
* @return
*/
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
/*public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException
{
LoginUser loginUser = tokenService.getLoginUser(request);
@ -49,5 +49,23 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, MessageUtils.message("user.logout.success")));
}
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success(MessageUtils.message("user.logout.success"))));
}*/
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException
{
LoginUser loginUser = tokenService.getLoginUser(request);
if (StringUtils.isNotNull(loginUser))
{
String userName = loginUser.getUsername();
// 删除用户缓存记录(旧方法)
//tokenService.delLoginUser(loginUser.getToken());
// 删除用户缓存记录 只允许单用户登录(关键)
tokenService.delLoginUser(loginUser.getToken(), loginUser.getUser().getUserId());
// 记录用户退出日志
AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功"));
}
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success("退出成功")));
}
}

@ -1,12 +1,5 @@
package com.da.framework.web.service;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.da.common.constant.CacheConstants;
import com.da.common.constant.Constants;
import com.da.common.constant.UserConstants;
@ -14,11 +7,7 @@ import com.da.common.core.domain.entity.SysUser;
import com.da.common.core.domain.model.LoginUser;
import com.da.common.core.redis.RedisCache;
import com.da.common.exception.ServiceException;
import com.da.common.exception.user.BlackListException;
import com.da.common.exception.user.CaptchaException;
import com.da.common.exception.user.CaptchaExpireException;
import com.da.common.exception.user.UserNotExistsException;
import com.da.common.exception.user.UserPasswordNotMatchException;
import com.da.common.exception.user.*;
import com.da.common.utils.DateUtils;
import com.da.common.utils.MessageUtils;
import com.da.common.utils.StringUtils;
@ -28,6 +17,15 @@ import com.da.framework.manager.factory.AsyncFactory;
import com.da.framework.security.context.AuthenticationContextHolder;
import com.da.system.service.ISysConfigService;
import com.da.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
*
@ -37,6 +35,9 @@ import com.da.system.service.ISysUserService;
@Component
public class SysLoginService
{
// 是否允许账户多终端同时登录true允许 false不允许
@Value("${token.soloLogin}")
private boolean soloLogin;
@Autowired
private TokenService tokenService;
@ -96,6 +97,21 @@ public class SysLoginService
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser.getUserId());
//---------------------------------------添加如下代码,验证如果用户不允许多终端同时登录,清除缓存信息
if (!soloLogin)
{
// 如果用户不允许多终端同时登录,清除缓存信息
String userIdKey = Constants.LOGIN_USERID_KEY + loginUser.getUser().getUserId();
String userKey = redisCache.getCacheObject(userIdKey);
if (StringUtils.isNotEmpty(userKey))
{
redisCache.deleteObject(userIdKey);
redisCache.deleteObject(userKey);
}
}
//----------------新增代码结束
// 生成token
return tokenService.createToken(loginUser);
}

@ -1,14 +1,5 @@
package com.da.framework.web.service;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.da.common.constant.CacheConstants;
import com.da.common.constant.Constants;
import com.da.common.core.domain.model.LoginUser;
@ -22,6 +13,16 @@ import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* token
@ -33,6 +34,10 @@ public class TokenService
{
private static final Logger log = LoggerFactory.getLogger(TokenService.class);
// 是否允许账户多终端同时登录true允许 false不允许
@Value("${token.soloLogin}")
private boolean soloLogin;
// 令牌自定义标识
@Value("${token.header}")
private String header;
@ -96,15 +101,29 @@ public class TokenService
/**
*
*/
public void delLoginUser(String token)
/* public void delLoginUser(String token)
{
if (StringUtils.isNotEmpty(token))
{
String userKey = getTokenKey(token);
redisCache.deleteObject(userKey);
}
}*/
public void delLoginUser(String token, Long userId)
{
if (StringUtils.isNotEmpty(token))
{
String userKey = getTokenKey(token);
redisCache.deleteObject(userKey);
}
if (!soloLogin && StringUtils.isNotNull(userId))
{
String userIdKey = getUserIdKey(userId);
redisCache.deleteObject(userIdKey);
}
}
/**
*
*
@ -144,13 +163,28 @@ public class TokenService
*
* @param loginUser
*/
public void refreshToken(LoginUser loginUser)
/* public void refreshToken(LoginUser loginUser)
{
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
}*/
public String refreshToken(LoginUser loginUser)
{
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
if (!soloLogin)
{
// 缓存用户唯一标识,防止同一帐号,同时登录
String userIdKey = getUserIdKey(loginUser.getUser().getUserId());
redisCache.setCacheObject(userIdKey, userKey, expireTime, TimeUnit.MINUTES);
}
return userKey;
}
/**
@ -168,6 +202,11 @@ public class TokenService
loginUser.setOs(userAgent.getOperatingSystem().getName());
}
private String getUserIdKey(Long userId)
{
return Constants.LOGIN_USERID_KEY + userId;
}
/**
*
*

@ -93,7 +93,8 @@ service.interceptors.response.use(res => {
isRelogin.show = false;
});
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
//return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
return Promise.reject('会话已过期,或者被挤下线,请重新登录。')
} else if (code === 500) {
Message({ message: msg, type: 'error' })
return Promise.reject(new Error(msg))

Loading…
Cancel
Save