|
|
@ -20,7 +20,9 @@ import cn.hutool.core.date.DateTime;
|
|
|
|
import cn.hutool.core.date.DateUnit;
|
|
|
|
import cn.hutool.core.date.DateUnit;
|
|
|
|
import cn.hutool.core.date.DateUtil;
|
|
|
|
import cn.hutool.core.date.DateUtil;
|
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
|
import com.google.common.collect.Maps;
|
|
|
|
import io.swagger.annotations.ApiModelProperty;
|
|
|
|
|
|
|
|
import lombok.Data;
|
|
|
|
|
|
|
|
import lombok.EqualsAndHashCode;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
import org.apache.shiro.crypto.hash.Md5Hash;
|
|
|
|
import org.apache.shiro.crypto.hash.Md5Hash;
|
|
|
@ -29,6 +31,7 @@ import org.opsli.api.wrapper.system.user.UserModel;
|
|
|
|
import org.opsli.common.constants.SignConstants;
|
|
|
|
import org.opsli.common.constants.SignConstants;
|
|
|
|
import org.opsli.common.constants.TokenConstants;
|
|
|
|
import org.opsli.common.constants.TokenConstants;
|
|
|
|
import org.opsli.common.constants.TokenTypeConstants;
|
|
|
|
import org.opsli.common.constants.TokenTypeConstants;
|
|
|
|
|
|
|
|
import org.opsli.common.enums.LoginLimitRefuse;
|
|
|
|
import org.opsli.common.exception.TokenException;
|
|
|
|
import org.opsli.common.exception.TokenException;
|
|
|
|
import org.opsli.core.autoconfigure.properties.GlobalProperties;
|
|
|
|
import org.opsli.core.autoconfigure.properties.GlobalProperties;
|
|
|
|
import org.opsli.core.cache.local.CacheUtil;
|
|
|
|
import org.opsli.core.cache.local.CacheUtil;
|
|
|
@ -41,7 +44,6 @@ import org.springframework.stereotype.Component;
|
|
|
|
|
|
|
|
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
|
import java.util.Date;
|
|
|
|
import java.util.Date;
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
|
|
|
|
import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
|
|
|
|
|
|
|
|
|
|
|
@ -65,13 +67,10 @@ public class UserTokenUtil {
|
|
|
|
public static final String ACCOUNT_SLIP_COUNT_PREFIX;
|
|
|
|
public static final String ACCOUNT_SLIP_COUNT_PREFIX;
|
|
|
|
/** 账号失败锁定KEY */
|
|
|
|
/** 账号失败锁定KEY */
|
|
|
|
public static final String ACCOUNT_SLIP_LOCK_PREFIX;
|
|
|
|
public static final String ACCOUNT_SLIP_LOCK_PREFIX;
|
|
|
|
/** 账号失败阈值 */
|
|
|
|
/** 限制登录数量 -1 为无限大 */
|
|
|
|
public static int ACCOUNT_SLIP_COUNT;
|
|
|
|
public static final int ACCOUNT_LIMIT_INFINITE = -1;
|
|
|
|
/** 账号失败N次后弹出验证码 */
|
|
|
|
/** 登录配置信息 */
|
|
|
|
public static int ACCOUNT_SLIP_VERIFY_COUNT;
|
|
|
|
public static GlobalProperties.Auth.Login LOGIN_PROPERTIES;
|
|
|
|
/** 账号锁定时间 */
|
|
|
|
|
|
|
|
public static int ACCOUNT_SLIP_LOCK_SPEED;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Redis插件 */
|
|
|
|
/** Redis插件 */
|
|
|
|
private static RedisPlugin redisPlugin;
|
|
|
|
private static RedisPlugin redisPlugin;
|
|
|
|
|
|
|
|
|
|
|
@ -89,15 +88,31 @@ public class UserTokenUtil {
|
|
|
|
* @param user 用户
|
|
|
|
* @param user 用户
|
|
|
|
* @return
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public static ResultVo<Map<String,Object>> createToken(UserModel user) {
|
|
|
|
public static ResultVo<UserTokenUtil.TokenRet> createToken(UserModel user) {
|
|
|
|
if (user == null) {
|
|
|
|
if (user == null) {
|
|
|
|
// 生成Token失败
|
|
|
|
// 生成Token失败
|
|
|
|
throw new TokenException(TokenMsg.EXCEPTION_TOKEN_CREATE_ERROR);
|
|
|
|
throw new TokenException(TokenMsg.EXCEPTION_TOKEN_CREATE_ERROR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Map<String,Object> map = Maps.newHashMapWithExpectedSize(2);
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 如果当前登录开启 数量限制
|
|
|
|
|
|
|
|
if(LOGIN_PROPERTIES.getLimitCount() > ACCOUNT_LIMIT_INFINITE){
|
|
|
|
|
|
|
|
// 当前用户已存在 Token数量
|
|
|
|
|
|
|
|
Long ticketLen = redisPlugin.sSize(TICKET_PREFIX + ":" + user.getUsername());
|
|
|
|
|
|
|
|
if(ticketLen !=null && ticketLen >= LOGIN_PROPERTIES.getLimitCount()){
|
|
|
|
|
|
|
|
// 如果是拒绝后者 则直接抛出异常
|
|
|
|
|
|
|
|
if(LoginLimitRefuse.AFTER == LOGIN_PROPERTIES.getLimitRefuse()){
|
|
|
|
|
|
|
|
// 生成Token失败 您的账号已在其他设备登录
|
|
|
|
|
|
|
|
throw new TokenException(TokenMsg.EXCEPTION_TOKEN_CREATE_LIMIT_ERROR);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 如果是拒绝前者 则弹出前者
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
redisPlugin.sPop(TICKET_PREFIX + ":" + user.getUsername());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 生效时间
|
|
|
|
// 生效时间
|
|
|
|
int expire = Integer.parseInt(
|
|
|
|
int expire = Integer.parseInt(
|
|
|
|
String.valueOf(JwtUtil.EXPIRE)
|
|
|
|
String.valueOf(JwtUtil.EXPIRE)
|
|
|
@ -106,9 +121,6 @@ public class UserTokenUtil {
|
|
|
|
// 生成 Token 包含 username userId timestamp
|
|
|
|
// 生成 Token 包含 username userId timestamp
|
|
|
|
String signToken = JwtUtil.sign(TokenTypeConstants.TYPE_SYSTEM, user.getUsername(), user.getId());
|
|
|
|
String signToken = JwtUtil.sign(TokenTypeConstants.TYPE_SYSTEM, user.getUsername(), user.getId());
|
|
|
|
|
|
|
|
|
|
|
|
// 生成MD5 16进制码 用于缩减存储
|
|
|
|
|
|
|
|
String signTokenHex = new Md5Hash(signToken).toHex();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获得当前时间戳时间
|
|
|
|
// 获得当前时间戳时间
|
|
|
|
long timestamp = Convert.toLong(
|
|
|
|
long timestamp = Convert.toLong(
|
|
|
|
JwtUtil.getClaim(signToken, SignConstants.TIMESTAMP));
|
|
|
|
JwtUtil.getClaim(signToken, SignConstants.TIMESTAMP));
|
|
|
@ -119,12 +131,19 @@ public class UserTokenUtil {
|
|
|
|
long endTimestamp = dateTime.getTime();
|
|
|
|
long endTimestamp = dateTime.getTime();
|
|
|
|
|
|
|
|
|
|
|
|
// 在redis存一份 token 是为了防止 人为造假
|
|
|
|
// 在redis存一份 token 是为了防止 人为造假
|
|
|
|
boolean tokenFlag = redisPlugin.put(TICKET_PREFIX + signTokenHex, endTimestamp, expire);
|
|
|
|
// 保存用户token
|
|
|
|
if(tokenFlag){
|
|
|
|
Long saveLong = redisPlugin.sPut(TICKET_PREFIX + ":" + user.getUsername(), signToken);
|
|
|
|
map.put("token", signToken);
|
|
|
|
if(saveLong != null && saveLong > 0){
|
|
|
|
map.put("expire", endTimestamp);
|
|
|
|
// 设置该用户全部token失效时间, 如果这时又有新设备登录 则续命
|
|
|
|
return ResultVo.success(map);
|
|
|
|
redisPlugin.expire(TICKET_PREFIX + ":" + user.getUsername(), expire);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TokenRet tokenRet = new TokenRet();
|
|
|
|
|
|
|
|
tokenRet.setToken(signToken);
|
|
|
|
|
|
|
|
tokenRet.setEndTimestamp(endTimestamp);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ResultVo.success(tokenRet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}catch (Exception e){
|
|
|
|
}catch (Exception e){
|
|
|
|
log.error(e.getMessage() , e);
|
|
|
|
log.error(e.getMessage() , e);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -175,19 +194,22 @@ public class UserTokenUtil {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
// 生成MD5 16进制码 用于缩减存储
|
|
|
|
// 获得要推出用户
|
|
|
|
String signTokenHex = new Md5Hash(token).toHex();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
redisPlugin.del(TICKET_PREFIX + signTokenHex);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 删除相关信息
|
|
|
|
|
|
|
|
String userId = getUserIdByToken(token);
|
|
|
|
String userId = getUserIdByToken(token);
|
|
|
|
UserModel user = UserUtil.getUser(userId);
|
|
|
|
UserModel user = UserUtil.getUser(userId);
|
|
|
|
if(user != null){
|
|
|
|
if(user != null){
|
|
|
|
UserUtil.refreshUser(user);
|
|
|
|
// 删除Token信息
|
|
|
|
UserUtil.refreshUserRoles(user.getId());
|
|
|
|
redisPlugin.sRemove(TICKET_PREFIX + ":" + user.getUsername(), token);
|
|
|
|
UserUtil.refreshUserAllPerms(user.getId());
|
|
|
|
|
|
|
|
UserUtil.refreshUserMenus(user.getId());
|
|
|
|
// 如果缓存中 无该用户任何Token信息 则删除用户缓存
|
|
|
|
|
|
|
|
Long size = redisPlugin.sSize(TICKET_PREFIX + ":" + user.getUsername());
|
|
|
|
|
|
|
|
if(size == null || size == 0L){
|
|
|
|
|
|
|
|
// 删除相关信息
|
|
|
|
|
|
|
|
UserUtil.refreshUser(user);
|
|
|
|
|
|
|
|
UserUtil.refreshUserRoles(user.getId());
|
|
|
|
|
|
|
|
UserUtil.refreshUserAllPerms(user.getId());
|
|
|
|
|
|
|
|
UserUtil.refreshUserMenus(user.getId());
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}catch (Exception ignored){}
|
|
|
|
}catch (Exception ignored){}
|
|
|
@ -211,12 +233,12 @@ public class UserTokenUtil {
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 校验当前缓存中token是否失效
|
|
|
|
// 2. 校验当前缓存中token是否失效
|
|
|
|
// 生成MD5 16进制码 用于缩减存储
|
|
|
|
// 生成MD5 16进制码 用于缩减存储
|
|
|
|
String signTokenHex = new Md5Hash(token).toHex();
|
|
|
|
// 删除相关信息
|
|
|
|
boolean hasKey = redisPlugin.hasKey(TICKET_PREFIX + signTokenHex);
|
|
|
|
String username = getUserNameByToken(token);
|
|
|
|
if(!hasKey){
|
|
|
|
boolean hashKey = redisPlugin.sHashKey(TICKET_PREFIX + ":" + username, token);
|
|
|
|
|
|
|
|
if(!hashKey){
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// JWT 自带过期校验 无需多做处理
|
|
|
|
// JWT 自带过期校验 无需多做处理
|
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e){
|
|
|
|
} catch (Exception e){
|
|
|
@ -238,7 +260,7 @@ public class UserTokenUtil {
|
|
|
|
Date currDate = new Date();
|
|
|
|
Date currDate = new Date();
|
|
|
|
DateTime loseDate = DateUtil.date(loseTimeMillis);
|
|
|
|
DateTime loseDate = DateUtil.date(loseTimeMillis);
|
|
|
|
// 偏移5分钟
|
|
|
|
// 偏移5分钟
|
|
|
|
DateTime currLoseDate = DateUtil.offsetSecond(loseDate, ACCOUNT_SLIP_LOCK_SPEED);
|
|
|
|
DateTime currLoseDate = DateUtil.offsetSecond(loseDate, LOGIN_PROPERTIES.getSlipLockSpeed());
|
|
|
|
|
|
|
|
|
|
|
|
// 计算失效剩余时间( 分 )
|
|
|
|
// 计算失效剩余时间( 分 )
|
|
|
|
long betweenM = DateUtil.between(currLoseDate, currDate, DateUnit.MINUTE);
|
|
|
|
long betweenM = DateUtil.between(currLoseDate, currDate, DateUnit.MINUTE);
|
|
|
@ -265,14 +287,14 @@ public class UserTokenUtil {
|
|
|
|
Long slipNum = redisPlugin.increment(ACCOUNT_SLIP_COUNT_PREFIX + username);
|
|
|
|
Long slipNum = redisPlugin.increment(ACCOUNT_SLIP_COUNT_PREFIX + username);
|
|
|
|
if (slipNum != null){
|
|
|
|
if (slipNum != null){
|
|
|
|
// 设置失效时间为 5分钟
|
|
|
|
// 设置失效时间为 5分钟
|
|
|
|
redisPlugin.expire(ACCOUNT_SLIP_COUNT_PREFIX + username, ACCOUNT_SLIP_LOCK_SPEED);
|
|
|
|
redisPlugin.expire(ACCOUNT_SLIP_COUNT_PREFIX + username, LOGIN_PROPERTIES.getSlipLockSpeed());
|
|
|
|
|
|
|
|
|
|
|
|
// 如果确认 都失败 则存入临时缓存
|
|
|
|
// 如果确认 都失败 则存入临时缓存
|
|
|
|
if(slipNum >= ACCOUNT_SLIP_COUNT){
|
|
|
|
if(slipNum >= LOGIN_PROPERTIES.getSlipCount()){
|
|
|
|
long currentTimeMillis = System.currentTimeMillis();
|
|
|
|
long currentTimeMillis = System.currentTimeMillis();
|
|
|
|
// 存入Redis
|
|
|
|
// 存入Redis
|
|
|
|
redisPlugin.put(ACCOUNT_SLIP_LOCK_PREFIX + username,
|
|
|
|
redisPlugin.put(ACCOUNT_SLIP_LOCK_PREFIX + username,
|
|
|
|
currentTimeMillis, ACCOUNT_SLIP_LOCK_SPEED);
|
|
|
|
currentTimeMillis, LOGIN_PROPERTIES.getSlipLockSpeed());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -334,18 +356,28 @@ public class UserTokenUtil {
|
|
|
|
if(globalProperties != null && globalProperties.getAuth() != null
|
|
|
|
if(globalProperties != null && globalProperties.getAuth() != null
|
|
|
|
&& globalProperties.getAuth().getLogin() != null
|
|
|
|
&& globalProperties.getAuth().getLogin() != null
|
|
|
|
){
|
|
|
|
){
|
|
|
|
// 账号失败阈值
|
|
|
|
// 登录配置信息
|
|
|
|
UserTokenUtil.ACCOUNT_SLIP_COUNT = globalProperties.getAuth()
|
|
|
|
UserTokenUtil.LOGIN_PROPERTIES = globalProperties.getAuth().getLogin();
|
|
|
|
.getLogin().getSlipCount();
|
|
|
|
|
|
|
|
// 账号失败N次后弹出验证码
|
|
|
|
|
|
|
|
UserTokenUtil.ACCOUNT_SLIP_VERIFY_COUNT = globalProperties.getAuth()
|
|
|
|
|
|
|
|
.getLogin().getSlipVerifyCount();
|
|
|
|
|
|
|
|
// 账号锁定时间
|
|
|
|
|
|
|
|
UserTokenUtil.ACCOUNT_SLIP_LOCK_SPEED = globalProperties.getAuth()
|
|
|
|
|
|
|
|
.getLogin().getSlipLockSpeed();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Redis 插件
|
|
|
|
// Redis 插件
|
|
|
|
UserTokenUtil.redisPlugin = redisPlugin;
|
|
|
|
UserTokenUtil.redisPlugin = redisPlugin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// =====================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Data
|
|
|
|
|
|
|
|
@EqualsAndHashCode(callSuper = false)
|
|
|
|
|
|
|
|
public static class TokenRet {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Token */
|
|
|
|
|
|
|
|
@ApiModelProperty(value = "Token")
|
|
|
|
|
|
|
|
private String token;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 失效时间戳 */
|
|
|
|
|
|
|
|
@ApiModelProperty(value = "失效时间戳")
|
|
|
|
|
|
|
|
private Long endTimestamp;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|