一、不分离版
在application.yml设置maxSession为1即可。修改shiro的配置
二、分离版springboot+vue
1、application.yml新增一个配置soloLogin用于限制多终端同时登录。修改token的配置。
2、Constants.java新增一个常量LOGIN_USERID_KEY公用。
3、调整TokenService.java,存储&刷新缓存用户编号信息
4、自定义退出处理类LogoutSuccessHandlerImpl.java清除缓存方法添加用户编号。
5、登录方法SysLoginService.java,验证如果用户不允许多终端同时登录,清除缓存信息。
一、不分离版
在application.yml设置maxSession为1即可。
修改shiro的配置
shiro:
session:
# 同一个用户最大会话数,比如2的意思是同一个账号允许最多同时两个人登录(默认-1不限制)
maxSession: 1
# 踢出之前登录的/之后登录的用户,默认踢出之前登录的用户
kickoutAfter: false
二、分离版springboot+vue
1、application.yml新增一个配置soloLogin用于限制多终端同时登录。
修改token的配置。
token:
# 是否允许账户多终端同时登录(true允许 false不允许)
soloLogin: false
2、Constants.java新增一个常量LOGIN_USERID_KEY公用。
路径:com.zhdj.common.constant.Constants.java。
/**
* 登录用户编号 redis key
*/
public static final String LOGIN_USERID_KEY = "login_userid:";
3、调整TokenService.java,存储&刷新缓存用户编号信息
路径:com.zhdj.framework.web.service.TokenService.java
// 是否允许账户多终端同时登录(true允许 false不允许)
@Value("${token.soloLogin}")
private boolean soloLogin;
/**
* 删除用户身份信息
*/
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);
}
}
/**
* 刷新令牌有效期
*
* @param 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);
//在原有代码上添加下面的代码
if (!soloLogin)
{
// 缓存用户唯一标识,防止同一帐号,同时登录
String userIdKey = getUserIdKey(loginUser.getUser().getUserId());
redisCache.setCacheObject(userIdKey, userKey, expireTime, TimeUnit.MINUTES);
}
}
//添加下面的代码
private String getUserIdKey(Long userId)
{
return Constants.LOGIN_USERID_KEY + userId;
}
4、自定义退出处理类LogoutSuccessHandlerImpl.java清除缓存方法添加用户编号。
路径:com.zhdj.framework.security.handle.LogoutSuccessHandlerImpl.java
// 删除用户缓存记录
// 在原代码参数中添加用户编号:loginUser.getUser().getUserId()
tokenService.delLoginUser(loginUser.getToken(), loginUser.getUser().getUserId());
5、登录方法SysLoginService.java,验证如果用户不允许多终端同时登录,清除缓存信息。
路径:com.zhdj.framework.web.service.SysLoginService.java
// 是否允许账户多终端同时登录(true允许 false不允许)
@Value("${token.soloLogin}")
private boolean soloLogin;
/**
* 登录验证
*
* @param username 用户名
* @param password 密码
* @param code 验证码
* @param uuid 唯一标识
* @return 结果
*/
public String login(String username, String password, String code, String uuid)
{
boolean captchaEnabled = configService.selectCaptchaEnabled();
// 验证码开关
if (captchaEnabled)
{
validateCaptcha(username, code, uuid);
}
// 用户验证
Authentication authentication = null;
try
{
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
AuthenticationContextHolder.setContext(authenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager.authenticate(authenticationToken);
}
catch (Exception e)
{
if (e instanceof BadCredentialsException)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
else
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
throw new ServiceException(e.getMessage());
}
}
finally
{
AuthenticationContextHolder.clearContext();
}
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);
}