perf:调整登录日志,同时限制登录失败次数(10分钟内最多5次)

This commit is contained in:
曾文豪
2024-08-28 20:35:38 +08:00
parent a9f218de89
commit 4690fa9f88
11 changed files with 175 additions and 49 deletions

View File

@@ -65,7 +65,7 @@ public class LoginController {
@OperationIgnore @OperationIgnore
public void uniqueIndex(UniqueIndexDTO dto, HttpServletResponse response) { public void uniqueIndex(UniqueIndexDTO dto, HttpServletResponse response) {
if (tsTokenConfig.isValidLoginSign() && !dto.validSign()) { if (tsTokenConfig.isValidLoginSign() && !dto.validSign()) {
corePlatformUniqueService.onSignError(response); corePlatformUniqueService.redirect(null, dto.getTo(), dto.getExtra(), response);
return; return;
} }
@@ -86,7 +86,7 @@ public class LoginController {
public ApiResp<String> uniqueIndex(@RequestBody UniqueIndexDTO dto) { public ApiResp<String> uniqueIndex(@RequestBody UniqueIndexDTO dto) {
TokenBean tokenBean = corePlatformUniqueService.login(new DoLoginInfo("web_unique_index", TokenBean tokenBean = corePlatformUniqueService.login(new DoLoginInfo("web_unique_index",
dto.getNo(), dto.getPlatform(), dto.getInfo())); dto.getNo(), dto.getPlatform(), dto.getInfo()));
if (tokenBean == null || StrUtil.isEmpty(tokenBean.getId())) { if (!TsTokenConfig.validToken(tokenBean)) {
throw new ApiException("登录失败"); throw new ApiException("登录失败");
} }
return ApiResp.respOK(tokenBean.toToken()); return ApiResp.respOK(tokenBean.toToken());
@@ -219,7 +219,7 @@ public class LoginController {
WxminiLoginVo loginVo = new WxminiLoginVo(); WxminiLoginVo loginVo = new WxminiLoginVo();
loginVo.setOpenid(openid); loginVo.setOpenid(openid);
if (tokenBean != null) { if (TsTokenConfig.validToken(tokenBean)) {
loginVo.setToken(tokenBean.toToken()); loginVo.setToken(tokenBean.toToken());
} }
return ApiResp.respOK(loginVo); return ApiResp.respOK(loginVo);

View File

@@ -2,6 +2,17 @@ package com.tiesheng.login.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tiesheng.login.pojos.dao.CoreLogLogin; import com.tiesheng.login.pojos.dao.CoreLogLogin;
import org.apache.ibatis.annotations.Param;
public interface CoreLogLoginMapper extends BaseMapper<CoreLogLogin> { public interface CoreLogLoginMapper extends BaseMapper<CoreLogLogin> {
/**
* 获取登录失败的次数
*
* @param ip
* @return
*/
int getLoginErrorTimes(@Param("ip") String ip);
} }

View File

@@ -1,5 +1,9 @@
package com.tiesheng.login.pojos; package com.tiesheng.login.pojos;
import com.tiesheng.util.ServletKit;
import javax.servlet.http.HttpServletRequest;
public class DoLoginInfo { public class DoLoginInfo {
private String appId; private String appId;
@@ -7,12 +11,17 @@ public class DoLoginInfo {
private String platform; private String platform;
private String info; private String info;
private String extra; private String extra;
private String loginIp;
public DoLoginInfo(String appId, String unique, String platform, String info) { public DoLoginInfo(String appId, String unique, String platform, String info) {
this.appId = appId; this.appId = appId;
this.unique = unique; this.unique = unique;
this.platform = platform; this.platform = platform;
this.info = info; this.info = info;
// 设置IP
HttpServletRequest request = ServletKit.getRequest();
this.loginIp = ServletKit.getClientIP(request);
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@@ -58,4 +67,12 @@ public class DoLoginInfo {
public void setExtra(String extra) { public void setExtra(String extra) {
this.extra = extra; this.extra = extra;
} }
public String getLoginIp() {
return loginIp;
}
public void setLoginIp(String loginIp) {
this.loginIp = loginIp;
}
} }

View File

@@ -16,13 +16,13 @@ public class CoreLogLogin extends DaoBase {
private String userId; private String userId;
/** /**
* 用户id * 用户姓名
*/ */
@TableField(value = "user_name") @TableField(value = "user_name")
private String userName; private String userName;
/** /**
* ip * 登录方式
*/ */
@TableField(value = "platform") @TableField(value = "platform")
private String platform; private String platform;
@@ -39,6 +39,18 @@ public class CoreLogLogin extends DaoBase {
@TableField(value = "address") @TableField(value = "address")
private String address; private String address;
/**
* 登录结果,0否1是
*/
@TableField(value = "`result`")
private Integer result;
/**
* 请求参数
*/
@TableField(value = "params")
private String params;
/** /**
* 获取用户id * 获取用户id
* *
@@ -57,18 +69,38 @@ public class CoreLogLogin extends DaoBase {
this.userId = userId; this.userId = userId;
} }
/**
* 获取用户姓名
*
* @return user_name - 用户姓名
*/
public String getUserName() { public String getUserName() {
return userName; return userName;
} }
/**
* 设置用户姓名
*
* @param userName 用户姓名
*/
public void setUserName(String userName) { public void setUserName(String userName) {
this.userName = userName; this.userName = userName;
} }
/**
* 获取登录方式
*
* @return platform - 登录方式
*/
public String getPlatform() { public String getPlatform() {
return platform; return platform;
} }
/**
* 设置登录方式
*
* @param platform 登录方式
*/
public void setPlatform(String platform) { public void setPlatform(String platform) {
this.platform = platform; this.platform = platform;
} }
@@ -108,4 +140,40 @@ public class CoreLogLogin extends DaoBase {
public void setAddress(String address) { public void setAddress(String address) {
this.address = address; this.address = address;
} }
/**
* 获取登录结果,0否1是
*
* @return result - 登录结果,0否1是
*/
public Integer getResult() {
return result;
}
/**
* 设置登录结果,0否1是
*
* @param result 登录结果,0否1是
*/
public void setResult(Integer result) {
this.result = result;
}
/**
* 获取请求参数
*
* @return params - 请求参数
*/
public String getParams() {
return params;
}
/**
* 设置请求参数
*
* @param params 请求参数
*/
public void setParams(String params) {
this.params = params;
}
} }

View File

@@ -1,14 +1,16 @@
package com.tiesheng.login.service; package com.tiesheng.login.service;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.tiesheng.login.mapper.CoreLogLoginMapper; import com.tiesheng.login.mapper.CoreLogLoginMapper;
import com.tiesheng.login.mapper.CorePlatformUniqueMapper; import com.tiesheng.login.mapper.CorePlatformUniqueMapper;
import com.tiesheng.login.pojos.DoLoginInfo; import com.tiesheng.login.pojos.DoLoginInfo;
import com.tiesheng.login.pojos.RequestUserInfo; import com.tiesheng.login.pojos.RequestUserInfo;
import com.tiesheng.login.pojos.dao.CoreLogLogin; import com.tiesheng.login.pojos.dao.CoreLogLogin;
import com.tiesheng.login.pojos.dao.CorePlatformUnique; import com.tiesheng.login.pojos.dao.CorePlatformUnique;
import com.tiesheng.util.ServletKit;
import com.tiesheng.util.config.Ip2regionConfig; import com.tiesheng.util.config.Ip2regionConfig;
import com.tiesheng.util.config.TsTokenConfig;
import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.pojos.TokenBean; import com.tiesheng.util.pojos.TokenBean;
import com.tiesheng.util.service.TsCacheService; import com.tiesheng.util.service.TsCacheService;
import com.tiesheng.util.service.TsServiceBase; import com.tiesheng.util.service.TsServiceBase;
@@ -17,7 +19,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.Objects; import java.util.Objects;
@@ -38,6 +39,11 @@ public class CorePlatformUniqueService extends TsServiceBase<CorePlatformUniqueM
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public TokenBean login(DoLoginInfo loginInfo) { public TokenBean login(DoLoginInfo loginInfo) {
int loginErrorTimes = coreLogLoginMapper.getLoginErrorTimes(loginInfo.getLoginIp());
if (loginErrorTimes > 4) {
throw new ApiException("登录失败已达5次请10分钟后再试");
}
CorePlatformUnique platformUnique = getOneByColumn("unique_id", loginInfo.getUnique()); CorePlatformUnique platformUnique = getOneByColumn("unique_id", loginInfo.getUnique());
if (platformUnique == null) { if (platformUnique == null) {
platformUnique = new CorePlatformUnique(); platformUnique = new CorePlatformUnique();
@@ -48,24 +54,33 @@ public class CorePlatformUniqueService extends TsServiceBase<CorePlatformUniqueM
platformUnique.setInfo(loginInfo.getInfo()); platformUnique.setInfo(loginInfo.getInfo());
saveOrUpdate(platformUnique); saveOrUpdate(platformUnique);
String oldUserId = platformUnique.getUserId(); TokenBean tokenBean = null;
TokenBean tokenBean = tsLoginConfigurer.login(platformUnique); String errorMsg = null;
if (tokenBean != null) { try {
tokenBean = tsLoginConfigurer.login(platformUnique);
} catch (Exception e) {
errorMsg = e.getMessage();
}
if (TsTokenConfig.validToken(tokenBean)) {
// 清除授权信息 // 清除授权信息
TsCacheService.of().remove(StrUtil.format(TsAuthorityHandler.CACHE_AUTHORITY, TsCacheService.of().remove(StrUtil.format(TsAuthorityHandler.CACHE_AUTHORITY,
tokenBean.getRoleId(), tokenBean.getId())); tokenBean.getRoleId(), tokenBean.getId()));
// 添加登录日志
addLoginLog(platformUnique, tokenBean);
// 更新唯一值 // 更新唯一值
String oldUserId = platformUnique.getUserId();
if (!StrUtil.isEmpty(tokenBean.getId()) && if (!StrUtil.isEmpty(tokenBean.getId()) &&
!Objects.equals(oldUserId, tokenBean.getId())) { !Objects.equals(oldUserId, tokenBean.getId())) {
platformUnique.setUserId(tokenBean.getId()); platformUnique.setUserId(tokenBean.getId());
saveOrUpdate(platformUnique); saveOrUpdate(platformUnique);
} }
}
addLoginLog(tokenBean, loginInfo);
if (StrUtil.isNotEmpty(errorMsg)) {
throw new ApiException(errorMsg);
} }
return tokenBean; return tokenBean;
@@ -78,40 +93,37 @@ public class CorePlatformUniqueService extends TsServiceBase<CorePlatformUniqueM
* @param bean * @param bean
*/ */
public void redirect(TokenBean bean, String to, String extra, HttpServletResponse response) { public void redirect(TokenBean bean, String to, String extra, HttpServletResponse response) {
tsLoginConfigurer.redirect(bean, to, extra, response); if (TsTokenConfig.validToken(bean)) {
tsLoginConfigurer.redirect(bean, to, extra, response);
} else {
tsLoginConfigurer.onLoginError(to, response);
}
} }
/**
* 签名错误的时候
*/
public void onSignError(HttpServletResponse response) {
tsLoginConfigurer.onSignError(response);
}
///////////////////////////////////////////////////////////////////////////
// 登录日志
///////////////////////////////////////////////////////////////////////////
/** /**
* 添加登录日志 * 添加登录日志
* *
* @param platformUnique
* @param tokenBean * @param tokenBean
* @param info
*/ */
public void addLoginLog(CorePlatformUnique platformUnique, TokenBean tokenBean) { public void addLoginLog(TokenBean tokenBean, DoLoginInfo info) {
HttpServletRequest request = ServletKit.getRequest();
String ip = ServletKit.getClientIP(request);
CoreLogLogin login = new CoreLogLogin(); CoreLogLogin login = new CoreLogLogin();
login.setUserId(tokenBean.getId()); login.setPlatform(info.getPlatform());
login.setPlatform(platformUnique.getPlatform()); login.setParams(JSON.toJSONString(info));
RequestUserInfo requestUserInfo = tsLoginConfigurer.getCachedUserInfo(tokenBean, true); if (TsTokenConfig.validToken(tokenBean)) {
login.setUserName(requestUserInfo.getName()); RequestUserInfo requestUserInfo = tsLoginConfigurer.getCachedUserInfo(tokenBean, true);
login.setUserId(tokenBean.getId());
login.setUserName(requestUserInfo.getName());
login.setResult(1);
} else {
login.setUserName(info.getUnique());
login.setResult(0);
}
login.setIp(ip); login.setIp(info.getLoginIp());
login.setAddress(ip2regionConfig.search(login.getIp())); login.setAddress(ip2regionConfig.search(login.getIp()));
coreLogLoginMapper.insert(login); coreLogLoginMapper.insert(login);
} }

View File

@@ -34,9 +34,9 @@ public interface TsLoginConfigurer {
/** /**
* 签名错误的时候 * 登录失败的时候
*/ */
default void onSignError(HttpServletResponse response) { default void onLoginError(String to,HttpServletResponse response) {
ServletKit.write(response, "404", "text"); ServletKit.write(response, "404", "text");
} }

View File

@@ -13,9 +13,20 @@
<result column="platform" jdbcType="VARCHAR" property="platform" /> <result column="platform" jdbcType="VARCHAR" property="platform" />
<result column="ip" jdbcType="VARCHAR" property="ip" /> <result column="ip" jdbcType="VARCHAR" property="ip" />
<result column="address" jdbcType="VARCHAR" property="address" /> <result column="address" jdbcType="VARCHAR" property="address" />
<result column="result" jdbcType="INTEGER" property="result" />
<result column="params" jdbcType="LONGVARCHAR" property="params" />
</resultMap> </resultMap>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
<!--@mbg.generated--> <!--@mbg.generated-->
id, create_time, update_time, is_deleted, user_id, user_name, platform, ip, address id, create_time, update_time, is_deleted, user_id, user_name, platform, ip, address,
`result`, params
</sql> </sql>
<select id="getLoginErrorTimes" resultType="int">
select count(1)
from core_log_login
where ip = #{ip}
and create_time > date_add(now(), interval -10 minute)
</select>
</mapper> </mapper>

View File

@@ -1,15 +1,12 @@
package com.tiesheng.util.config; package com.tiesheng.util.config;
import cn.hutool.core.codec.Base64; import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil; import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4; import cn.hutool.crypto.symmetric.SM4;
import com.tiesheng.util.ServletKit;
import com.tiesheng.util.exception.ApiException; import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.service.TsCacheService;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -104,12 +101,6 @@ public class EncryptConfig {
String salt = decrypt(encrypted).substring(0, saltSize); String salt = decrypt(encrypted).substring(0, saltSize);
String inputEncrypted = passwdCreate(inputPasswd, salt); String inputEncrypted = passwdCreate(inputPasswd, salt);
if (!StrUtil.equals(inputEncrypted, encrypted)) { if (!StrUtil.equals(inputEncrypted, encrypted)) {
String clientIp = "passwdVerify_" + ServletKit.getClientIP();
int num = NumberUtil.parseInt(TsCacheService.of().get(clientIp, -1));
if (num > 5) {
throw new ApiException("登录失败已达6次请10分钟后再试");
}
TsCacheService.of().put(clientIp, String.valueOf(num + 1), 10 * 60);
throw new ApiException("账号或密码错误"); throw new ApiException("账号或密码错误");
} }
} }

View File

@@ -66,6 +66,16 @@ public class TsTokenConfig {
return validToken(token, thrExp); return validToken(token, thrExp);
} }
/**
* token是否有效
*
* @param token
* @return
*/
public static boolean validToken(TokenBean token) {
return token != null && StrUtil.isNotEmpty(token.getId());
}
/** /**
* 验证token * 验证token

View File

@@ -1,7 +1,6 @@
package com.tiesheng.web; package com.tiesheng.web;
import com.tiesheng.database.DatabaseAutoConfigurer; import com.tiesheng.database.DatabaseAutoConfigurer;
import com.tiesheng.encrypt.EncryptAutoConfigurer;
import com.tiesheng.message.MessageAutoConfigurer; import com.tiesheng.message.MessageAutoConfigurer;
import com.tiesheng.platform.PlatformAutoConfigurer; import com.tiesheng.platform.PlatformAutoConfigurer;
import com.tiesheng.util.UtilAutoConfigurer; import com.tiesheng.util.UtilAutoConfigurer;
@@ -21,7 +20,6 @@ import java.lang.annotation.*;
MessageAutoConfigurer.class, MessageAutoConfigurer.class,
WebAutoConfigurer.class, WebAutoConfigurer.class,
DatabaseAutoConfigurer.class, DatabaseAutoConfigurer.class,
EncryptAutoConfigurer.class,
PlatformAutoConfigurer.class, PlatformAutoConfigurer.class,
}) })
public @interface EnableTieshengWeb { public @interface EnableTieshengWeb {

View File

@@ -78,4 +78,12 @@ alter table core_log_process
alter table core_log_process alter table core_log_process
change fail_file result_file varchar(500) null comment '失败的文件'; change fail_file result_file varchar(500) null comment '失败的文件';
## 2.0.2
alter table core_log_login
add result int(10) not null default 1 comment '登录结果,0否1是';
alter table core_log_login
add params longtext null comment '请求参数';
SET FOREIGN_KEY_CHECKS = 1; SET FOREIGN_KEY_CHECKS = 1;