|
|
@ -15,11 +15,9 @@
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
package org.opsli.modulars.system.login.web;
|
|
|
|
package org.opsli.modulars.system.login.web;
|
|
|
|
|
|
|
|
|
|
|
|
import cn.hutool.core.date.DateTime;
|
|
|
|
import cn.hutool.core.convert.Convert;
|
|
|
|
import cn.hutool.core.date.DateUnit;
|
|
|
|
|
|
|
|
import cn.hutool.core.date.DateUtil;
|
|
|
|
|
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
|
|
|
|
|
import com.google.common.collect.Maps;
|
|
|
|
import io.swagger.annotations.ApiOperation;
|
|
|
|
import io.swagger.annotations.ApiOperation;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
@ -27,6 +25,7 @@ import org.apache.tomcat.util.http.fileupload.IOUtils;
|
|
|
|
import org.opsli.api.base.result.ResultVo;
|
|
|
|
import org.opsli.api.base.result.ResultVo;
|
|
|
|
import org.opsli.api.wrapper.system.user.UserModel;
|
|
|
|
import org.opsli.api.wrapper.system.user.UserModel;
|
|
|
|
import org.opsli.common.api.TokenThreadLocal;
|
|
|
|
import org.opsli.common.api.TokenThreadLocal;
|
|
|
|
|
|
|
|
import org.opsli.common.exception.TokenException;
|
|
|
|
import org.opsli.common.utils.IPUtil;
|
|
|
|
import org.opsli.common.utils.IPUtil;
|
|
|
|
import org.opsli.core.msg.TokenMsg;
|
|
|
|
import org.opsli.core.msg.TokenMsg;
|
|
|
|
import org.opsli.core.persistence.querybuilder.GenQueryBuilder;
|
|
|
|
import org.opsli.core.persistence.querybuilder.GenQueryBuilder;
|
|
|
@ -38,9 +37,7 @@ import org.opsli.modulars.system.login.entity.LoginForm;
|
|
|
|
import org.opsli.modulars.system.tenant.entity.SysTenant;
|
|
|
|
import org.opsli.modulars.system.tenant.entity.SysTenant;
|
|
|
|
import org.opsli.modulars.system.tenant.service.ITenantService;
|
|
|
|
import org.opsli.modulars.system.tenant.service.ITenantService;
|
|
|
|
import org.opsli.modulars.system.user.service.IUserService;
|
|
|
|
import org.opsli.modulars.system.user.service.IUserService;
|
|
|
|
import org.opsli.plugins.redis.RedisPlugin;
|
|
|
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
|
|
|
|
|
import org.springframework.web.bind.annotation.GetMapping;
|
|
|
|
import org.springframework.web.bind.annotation.GetMapping;
|
|
|
|
import org.springframework.web.bind.annotation.PostMapping;
|
|
|
|
import org.springframework.web.bind.annotation.PostMapping;
|
|
|
|
import org.springframework.web.bind.annotation.RequestBody;
|
|
|
|
import org.springframework.web.bind.annotation.RequestBody;
|
|
|
@ -52,7 +49,6 @@ import javax.servlet.http.HttpServletRequest;
|
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
|
import java.awt.image.BufferedImage;
|
|
|
|
import java.awt.image.BufferedImage;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.Date;
|
|
|
|
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
|
@ -69,93 +65,52 @@ import java.util.Map;
|
|
|
|
@RestController
|
|
|
|
@RestController
|
|
|
|
public class LoginRestController {
|
|
|
|
public class LoginRestController {
|
|
|
|
|
|
|
|
|
|
|
|
/** 账号失败次数 */
|
|
|
|
|
|
|
|
private static final String ACCOUNT_SLIP_COUNT_PREFIX = "opsli:account:slip:count:";
|
|
|
|
|
|
|
|
/** 账号失败锁定KEY */
|
|
|
|
|
|
|
|
private static final String ACCOUNT_SLIP_LOCK_PREFIX = "opsli:account:slip:lock:";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 失败阈值 */
|
|
|
|
|
|
|
|
@Value("${opsli.login.slip-count}")
|
|
|
|
|
|
|
|
private int slipCount;
|
|
|
|
|
|
|
|
/** 锁定时间 */
|
|
|
|
|
|
|
|
@Value("${opsli.login.slip-lock-speed}")
|
|
|
|
|
|
|
|
private int slipLockSpeed;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
|
|
private RedisPlugin redisPlugin;
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
@Autowired
|
|
|
|
private ITenantService iTenantService;
|
|
|
|
private ITenantService iTenantService;
|
|
|
|
@Autowired
|
|
|
|
@Autowired
|
|
|
|
private IUserService iUserService;
|
|
|
|
private IUserService iUserService;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 登录
|
|
|
|
* 登录
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
@ApiOperation(value = "登录", notes = "登录")
|
|
|
|
@ApiOperation(value = "登录", notes = "登录")
|
|
|
|
@PostMapping("/sys/login")
|
|
|
|
@PostMapping("/sys/login")
|
|
|
|
public ResultVo<?> login(@RequestBody LoginForm form, HttpServletRequest request){
|
|
|
|
public ResultVo<?> login(@RequestBody LoginForm form, HttpServletRequest request){
|
|
|
|
|
|
|
|
if(form == null){
|
|
|
|
|
|
|
|
throw new TokenException(TokenMsg.EXCEPTION_LOGIN_NULL);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 判断账号是否临时锁定
|
|
|
|
|
|
|
|
UserTokenUtil.verifyLockAccount(form.getUsername());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获得当前失败次数
|
|
|
|
|
|
|
|
long slipCount = UserTokenUtil.getSlipCount(form.getUsername());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 失败次数超过 验证次数阈值 开启验证码验证
|
|
|
|
|
|
|
|
if(slipCount >= UserTokenUtil.ACCOUNT_SLIP_VERIFY_COUNT){
|
|
|
|
boolean captcha = CaptchaUtil.validate(form.getUuid(), form.getCaptcha());
|
|
|
|
boolean captcha = CaptchaUtil.validate(form.getUuid(), form.getCaptcha());
|
|
|
|
// 验证码不正确
|
|
|
|
// 验证码不正确
|
|
|
|
if(!captcha){
|
|
|
|
if(!captcha){
|
|
|
|
return ResultVo.error(TokenMsg.EXCEPTION_LOGIN_CAPTCHA.getCode(),
|
|
|
|
throw new TokenException(TokenMsg.EXCEPTION_LOGIN_CAPTCHA);
|
|
|
|
TokenMsg.EXCEPTION_LOGIN_CAPTCHA.getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 判断账号是否临时锁定
|
|
|
|
// 用户信息
|
|
|
|
Long loseTimeMillis = (Long) redisPlugin.get(ACCOUNT_SLIP_LOCK_PREFIX + form.getUsername());
|
|
|
|
|
|
|
|
if(loseTimeMillis != null){
|
|
|
|
|
|
|
|
Date currDate = new Date();
|
|
|
|
|
|
|
|
DateTime loseDate = DateUtil.date(loseTimeMillis);
|
|
|
|
|
|
|
|
// 偏移5分钟
|
|
|
|
|
|
|
|
DateTime currLoseDate = DateUtil.offsetSecond(loseDate, slipLockSpeed);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算失效剩余时间( 分 )
|
|
|
|
|
|
|
|
long betweenM = DateUtil.between(currLoseDate, currDate, DateUnit.MINUTE);
|
|
|
|
|
|
|
|
if(betweenM > 0){
|
|
|
|
|
|
|
|
String msg = StrUtil.format(TokenMsg.EXCEPTION_LOGIN_ACCOUNT_LOCK.getMessage()
|
|
|
|
|
|
|
|
,betweenM + "分钟");
|
|
|
|
|
|
|
|
return ResultVo.error(TokenMsg.EXCEPTION_LOGIN_ACCOUNT_LOCK.getCode(),
|
|
|
|
|
|
|
|
msg);
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
|
|
|
// 计算失效剩余时间( 秒 )
|
|
|
|
|
|
|
|
long betweenS = DateUtil.between(currLoseDate, currDate, DateUnit.SECOND);
|
|
|
|
|
|
|
|
String msg = StrUtil.format(TokenMsg.EXCEPTION_LOGIN_ACCOUNT_LOCK.getMessage()
|
|
|
|
|
|
|
|
,betweenS + "秒");
|
|
|
|
|
|
|
|
return ResultVo.error(TokenMsg.EXCEPTION_LOGIN_ACCOUNT_LOCK.getCode(),
|
|
|
|
|
|
|
|
msg);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//用户信息
|
|
|
|
|
|
|
|
UserModel user = UserUtil.getUserByUserName(form.getUsername());
|
|
|
|
UserModel user = UserUtil.getUserByUserName(form.getUsername());
|
|
|
|
|
|
|
|
|
|
|
|
//账号不存在、密码错误
|
|
|
|
// 账号不存在、密码错误
|
|
|
|
if(user == null ||
|
|
|
|
if(user == null ||
|
|
|
|
!user.getPassword().equals(UserUtil.handlePassword(form.getPassword(), user.getSecretkey()))) {
|
|
|
|
!user.getPassword().equals(UserUtil.handlePassword(form.getPassword(), user.getSecretkey()))) {
|
|
|
|
|
|
|
|
// 判断是否需要锁定账号 这里没有直接抛异常 而是返回错误信息, 其中包含 是否开启验证码状态
|
|
|
|
// 如果失败次数 超过阈值 则锁定账号
|
|
|
|
return UserTokenUtil.lockAccount(form.getUsername());
|
|
|
|
Long slipNum = redisPlugin.increment(ACCOUNT_SLIP_COUNT_PREFIX + form.getUsername());
|
|
|
|
|
|
|
|
// 设置失效时间为 5分钟
|
|
|
|
|
|
|
|
redisPlugin.expire(ACCOUNT_SLIP_COUNT_PREFIX + form.getUsername(),slipLockSpeed);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 如果确认 都失败 则存入临时缓存
|
|
|
|
|
|
|
|
if(slipNum >= slipCount){
|
|
|
|
|
|
|
|
long currentTimeMillis = System.currentTimeMillis();
|
|
|
|
|
|
|
|
// 存入Redis
|
|
|
|
|
|
|
|
redisPlugin.put(ACCOUNT_SLIP_LOCK_PREFIX + form.getUsername(),
|
|
|
|
|
|
|
|
currentTimeMillis, slipLockSpeed);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ResultVo.error(TokenMsg.EXCEPTION_LOGIN_ACCOUNT_NO.getMessage());
|
|
|
|
// 如果验证成功, 则清除锁定信息
|
|
|
|
}
|
|
|
|
UserTokenUtil.clearLockAccount(form.getUsername());
|
|
|
|
|
|
|
|
|
|
|
|
// 删除失败次数记录
|
|
|
|
// 账号锁定
|
|
|
|
redisPlugin.del(ACCOUNT_SLIP_COUNT_PREFIX + form.getUsername());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//账号锁定
|
|
|
|
|
|
|
|
if(user.getLocked() == 1){
|
|
|
|
if(user.getLocked() == 1){
|
|
|
|
return ResultVo.error(TokenMsg.EXCEPTION_LOGIN_ACCOUNT_LOCKED.getMessage());
|
|
|
|
throw new TokenException(TokenMsg.EXCEPTION_LOGIN_ACCOUNT_LOCKED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果不是超级管理员 需要验证租户是否生效
|
|
|
|
// 如果不是超级管理员 需要验证租户是否生效
|
|
|
@ -166,12 +121,15 @@ public class LoginRestController {
|
|
|
|
.eq("iz_usable", "1");
|
|
|
|
.eq("iz_usable", "1");
|
|
|
|
List<SysTenant> tenants = iTenantService.findList(queryWrapper);
|
|
|
|
List<SysTenant> tenants = iTenantService.findList(queryWrapper);
|
|
|
|
if(tenants == null || tenants.isEmpty()){
|
|
|
|
if(tenants == null || tenants.isEmpty()){
|
|
|
|
return ResultVo.error(TokenMsg.EXCEPTION_LOGIN_TENANT_NOT_USABLE.getMessage());
|
|
|
|
throw new TokenException(TokenMsg.EXCEPTION_LOGIN_TENANT_NOT_USABLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 失败次数超过 验证次数阈值 开启验证码验证
|
|
|
|
|
|
|
|
if(slipCount >= UserTokenUtil.ACCOUNT_SLIP_VERIFY_COUNT){
|
|
|
|
// 删除验证过后验证码
|
|
|
|
// 删除验证过后验证码
|
|
|
|
CaptchaUtil.delCaptcha(form.getUuid());
|
|
|
|
CaptchaUtil.delCaptcha(form.getUuid());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//生成token,并保存到Redis
|
|
|
|
//生成token,并保存到Redis
|
|
|
|
ResultVo<Map<String, Object>> resultVo = UserTokenUtil.createToken(user);
|
|
|
|
ResultVo<Map<String, Object>> resultVo = UserTokenUtil.createToken(user);
|
|
|
@ -183,8 +141,8 @@ public class LoginRestController {
|
|
|
|
String clientIpAddress = IPUtil.getClientIpAddress(request);
|
|
|
|
String clientIpAddress = IPUtil.getClientIpAddress(request);
|
|
|
|
user.setLoginIp(clientIpAddress);
|
|
|
|
user.setLoginIp(clientIpAddress);
|
|
|
|
iUserService.updateLoginIp(user);
|
|
|
|
iUserService.updateLoginIp(user);
|
|
|
|
}catch (Exception ignored){}
|
|
|
|
}catch (Exception ignored){
|
|
|
|
finally {
|
|
|
|
}finally {
|
|
|
|
// 清空 token缓存
|
|
|
|
// 清空 token缓存
|
|
|
|
TokenThreadLocal.remove();
|
|
|
|
TokenThreadLocal.remove();
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -209,6 +167,21 @@ public class LoginRestController {
|
|
|
|
return ResultVo.success(TokenMsg.EXCEPTION_LOGOUT_SUCCESS.getMessage());
|
|
|
|
return ResultVo.success(TokenMsg.EXCEPTION_LOGOUT_SUCCESS.getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 获得当前登录失败次数
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@ApiOperation(value = "获得当前登录失败次数", notes = "获得当前登录失败次数")
|
|
|
|
|
|
|
|
@GetMapping("/sys/slipCount")
|
|
|
|
|
|
|
|
public ResultVo<?> slipCount(String username){
|
|
|
|
|
|
|
|
// 获得当前失败次数
|
|
|
|
|
|
|
|
long slipCount = UserTokenUtil.getSlipCount(username);
|
|
|
|
|
|
|
|
Map<String, Object> ret = Maps.newHashMap();
|
|
|
|
|
|
|
|
ret.put("base", UserTokenUtil.ACCOUNT_SLIP_VERIFY_COUNT);
|
|
|
|
|
|
|
|
ret.put("curr", slipCount);
|
|
|
|
|
|
|
|
return ResultVo.success(ret);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 验证码
|
|
|
|
* 验证码
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|