This commit is contained in:
777
2025-12-05 18:27:51 +08:00
parent 84d42a3af2
commit 513954ccf1
17 changed files with 226 additions and 11 deletions

View File

@@ -1,6 +1,7 @@
CREATE TABLE `cai_ip_record` CREATE TABLE `cai_ip_record`
( (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '子账户ID', `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '子账户ID',
`count_date` date NOT NULL COMMENT '统计日期',
`ip_addr` varchar(100) NOT NULL COMMENT 'IP地址', `ip_addr` varchar(100) NOT NULL COMMENT 'IP地址',
`path_addr` varchar(255) NULL COMMENT '请求地址', `path_addr` varchar(255) NULL COMMENT '请求地址',
`business_id` varchar(255) NULL, `business_id` varchar(255) NULL,

View File

@@ -10,6 +10,8 @@ import com.ruoyi.cai.kit.VerificationCodeCheck;
import com.ruoyi.cai.manager.CurrentUserManager; import com.ruoyi.cai.manager.CurrentUserManager;
import com.ruoyi.cai.manager.LoginAfterManager; import com.ruoyi.cai.manager.LoginAfterManager;
import com.ruoyi.cai.manager.SystemConfigManager; import com.ruoyi.cai.manager.SystemConfigManager;
import com.ruoyi.cai.service.IpBlackService;
import com.ruoyi.cai.service.IpRecordService;
import com.ruoyi.cai.service.SmsVerifyService; import com.ruoyi.cai.service.SmsVerifyService;
import com.ruoyi.cai.service.UserService; import com.ruoyi.cai.service.UserService;
import com.ruoyi.common.annotation.Log; import com.ruoyi.common.annotation.Log;
@@ -142,15 +144,24 @@ public class AuthAppController {
@Autowired @Autowired
private LoginAfterManager loginAfterManager; private LoginAfterManager loginAfterManager;
@Autowired
private IpRecordService ipRecordService;
@Autowired
private IpBlackService ipBlackService;
@PostMapping("/login") @PostMapping("/login")
@Operation(summary = "登陆") @Operation(summary = "登陆")
@Log(title = "登陆", businessType = BusinessType.OTHER, isSaveDb = false) @Log(title = "登陆", businessType = BusinessType.OTHER, isSaveDb = false)
public R<LoginVo> login(@Validated @RequestBody LoginCaiUser loginBody){ public R<LoginVo> login(@Validated @RequestBody LoginCaiUser loginBody){
LoginVo vo = new LoginVo(); LoginVo vo = new LoginVo();
String token = caiLoginManager.login(loginBody.getUsername(), loginBody.getPassword()); try {
vo.setToken(token); String token = caiLoginManager.login(loginBody.getUsername(), loginBody.getPassword());
vo.setUserInfo(currentUserManager.currentInfo()); vo.setToken(token);
vo.setUserInfo(currentUserManager.currentInfo());
}catch (Exception e){
ipRecordService.saveLoginIp(ServletUtils.getClientIP());
throw e;
}
// 异步调用通知 // 异步调用通知
// loginAfterManager.loginAfter(LoginHelper.getUserId()); // loginAfterManager.loginAfter(LoginHelper.getUserId());
return R.ok(vo); return R.ok(vo);

View File

@@ -93,15 +93,24 @@ public class CaiLoginManager {
private IgnoreDataService ignoreDataService; private IgnoreDataService ignoreDataService;
@Autowired @Autowired
private AmqpHttpProducer amqpHttpProducer; private AmqpHttpProducer amqpHttpProducer;
@Autowired
private IpRecordService ipRecordService;
@Autowired
private IpBlackService ipBlackService;
public String login(String username,String password){ public String login(String username,String password){
String clientIP = ServletUtils.getClientIP();
Boolean b = ipBlackService.checkIp(clientIP);
if(b){
log.error("登录拦截了异常IP={}", clientIP);
throw new ServiceException("40305");
}
User user = userService.getByUsername(username); User user = userService.getByUsername(username);
if(user == null){ if(user == null){
throw new ServiceException("用户不存在或密码错误"); throw new ServiceException("用户不存在或密码错误");
} }
String imei = ServletUtils.getImei(); String imei = ServletUtils.getImei();
UserForbidManager.CheckForbid forbid = userForbidManager.checkForbid(user.getId(), user.getUsercode(), imei, ServletUtils.getClientIP()); UserForbidManager.CheckForbid forbid = userForbidManager.checkForbid(user.getId(), user.getUsercode(), imei, clientIP);
if(forbid != null && forbid.isForbid()){ if(forbid != null && forbid.isForbid()){
throw new ServiceException(forbid.getMessage()); throw new ServiceException(forbid.getMessage());
} }

View File

@@ -64,7 +64,9 @@ public class IpBlackController extends BaseController {
@RepeatSubmit() @RepeatSubmit()
@PostMapping() @PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody IpBlack bo) { public R<Void> add(@Validated(AddGroup.class) @RequestBody IpBlack bo) {
return toAjax(ipBlackService.save(bo)); bo.setEnableStatus(1);
ipBlackService.saveIp(bo);
return R.ok();
} }
/** /**

View File

@@ -1,9 +1,11 @@
package com.ruoyi.cai.controller; package com.ruoyi.cai.controller;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.cai.domain.IpRecord; import com.ruoyi.cai.domain.IpRecord;
import com.ruoyi.cai.dto.admin.vo.IpRecordAdminVO;
import com.ruoyi.cai.service.IpRecordService; import com.ruoyi.cai.service.IpRecordService;
import com.ruoyi.common.annotation.Log; import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit; import com.ruoyi.common.annotation.RepeatSubmit;
@@ -41,8 +43,8 @@ public class IpRecordController extends BaseController {
*/ */
@SaCheckPermission("cai:ipRecord:list") @SaCheckPermission("cai:ipRecord:list")
@GetMapping("/list") @GetMapping("/list")
public TableDataInfo<IpRecord> list(IpRecord bo, PageQuery pageQuery) { public TableDataInfo<IpRecordAdminVO> list(IpRecordAdminVO bo, PageQuery pageQuery) {
Page<IpRecord> page = ipRecordService.page(pageQuery.build(), Wrappers.lambdaQuery(bo)); IPage<IpRecordAdminVO> page = ipRecordService.pageAdmin(bo,pageQuery);
return TableDataInfo.build(page); return TableDataInfo.build(page);
} }

View File

@@ -32,7 +32,7 @@ public class IpBlack implements Serializable {
/** /**
* 是否开启 * 是否开启
*/ */
private Long enableStatus; private Integer enableStatus;
private LocalDateTime createTime; private LocalDateTime createTime;

View File

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
/** /**
@@ -25,6 +26,7 @@ public class IpRecord implements Serializable{
*/ */
@TableId(value = "id",type = IdType.AUTO) @TableId(value = "id",type = IdType.AUTO)
private String id; private String id;
private LocalDate countDate;
/** /**
* IP地址 * IP地址
*/ */

View File

@@ -0,0 +1,10 @@
package com.ruoyi.cai.dto.admin.vo;
import com.ruoyi.cai.domain.IpRecord;
import lombok.Data;
@Data
public class IpRecordAdminVO extends IpRecord {
private boolean black;
private Integer minNumber;
}

View File

@@ -6,6 +6,7 @@ import lombok.Data;
public class MinUser { public class MinUser {
private Long id; private Long id;
private Integer gender; private Integer gender;
private String mobile;
/** /**
* 隐身模式 0-关闭 1-打开 * 隐身模式 0-关闭 1-打开
*/ */

View File

@@ -1,7 +1,11 @@
package com.ruoyi.cai.mapper; package com.ruoyi.cai.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.cai.domain.IpRecord; import com.ruoyi.cai.domain.IpRecord;
import com.ruoyi.cai.dto.admin.vo.IpRecordAdminVO;
import org.apache.ibatis.annotations.Param;
/** /**
* ip访问记录Mapper接口 * ip访问记录Mapper接口
@@ -11,4 +15,5 @@ import com.ruoyi.cai.domain.IpRecord;
*/ */
public interface IpRecordMapper extends BaseMapper<IpRecord> { public interface IpRecordMapper extends BaseMapper<IpRecord> {
IPage<IpRecordAdminVO> pageAdmin(Page<Object> build, @Param("bo") IpRecordAdminVO bo);
} }

View File

@@ -11,4 +11,7 @@ import com.ruoyi.cai.domain.IpBlack;
*/ */
public interface IpBlackService extends IService<IpBlack> { public interface IpBlackService extends IService<IpBlack> {
Boolean checkIp(String clientIP);
void saveIp(IpBlack ipBlack);
} }

View File

@@ -1,7 +1,10 @@
package com.ruoyi.cai.service; package com.ruoyi.cai.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.cai.domain.IpRecord; import com.ruoyi.cai.domain.IpRecord;
import com.ruoyi.cai.dto.admin.vo.IpRecordAdminVO;
import com.ruoyi.common.core.domain.PageQuery;
/** /**
* ip访问记录Service接口 * ip访问记录Service接口
@@ -10,4 +13,7 @@ import com.ruoyi.cai.domain.IpRecord;
* @date 2025-12-05 * @date 2025-12-05
*/ */
public interface IpRecordService extends IService<IpRecord> { public interface IpRecordService extends IService<IpRecord> {
IPage<IpRecordAdminVO> pageAdmin(IpRecordAdminVO bo, PageQuery pageQuery);
void saveLoginIp(String clientIP);
} }

View File

@@ -1,9 +1,11 @@
package com.ruoyi.cai.service.impl; package com.ruoyi.cai.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.cai.domain.IpBlack; import com.ruoyi.cai.domain.IpBlack;
import com.ruoyi.cai.mapper.IpBlackMapper; import com.ruoyi.cai.mapper.IpBlackMapper;
import com.ruoyi.cai.service.IpBlackService; import com.ruoyi.cai.service.IpBlackService;
import com.ruoyi.common.exception.ServiceException;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -17,4 +19,20 @@ import org.springframework.stereotype.Service;
@Service @Service
public class IpBlackServiceImpl extends ServiceImpl<IpBlackMapper,IpBlack> implements IpBlackService { public class IpBlackServiceImpl extends ServiceImpl<IpBlackMapper,IpBlack> implements IpBlackService {
@Override
public Boolean checkIp(String clientIP) {
boolean exists = this.exists(Wrappers.lambdaQuery(IpBlack.class).eq(IpBlack::getIpAddr, clientIP)
.eq(IpBlack::getEnableStatus, 1));
return exists;
}
@Override
public void saveIp(IpBlack ipBlack){
boolean exists = this.exists(Wrappers.lambdaQuery(IpBlack.class).eq(IpBlack::getIpAddr, ipBlack.getIpAddr()));
if(exists){
throw new ServiceException("IP已经存在");
}
this.save(ipBlack);
}
} }

View File

@@ -1,12 +1,25 @@
package com.ruoyi.cai.service.impl; package com.ruoyi.cai.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.cai.domain.IpRecord; import com.ruoyi.cai.domain.IpRecord;
import com.ruoyi.cai.dto.admin.vo.IpRecordAdminVO;
import com.ruoyi.cai.mapper.IpRecordMapper; import com.ruoyi.cai.mapper.IpRecordMapper;
import com.ruoyi.cai.service.IpRecordService; import com.ruoyi.cai.service.IpRecordService;
import com.ruoyi.common.core.domain.PageQuery;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;
/** /**
* ip访问记录Service业务层处理 * ip访问记录Service业务层处理
* *
@@ -17,4 +30,109 @@ import org.springframework.stereotype.Service;
@Service @Service
public class IpRecordServiceImpl extends ServiceImpl<IpRecordMapper,IpRecord> implements IpRecordService { public class IpRecordServiceImpl extends ServiceImpl<IpRecordMapper,IpRecord> implements IpRecordService {
@Autowired
private RedissonClient redissonClient;
@Override
public IPage<IpRecordAdminVO> pageAdmin(IpRecordAdminVO bo, PageQuery pageQuery){
IPage<IpRecordAdminVO> page = baseMapper.pageAdmin(pageQuery.build(), bo);
return page;
}
@Override
public void saveLoginIp(String ip) {
long l = saveIp(ip);
if(l < 5){
return;
}
if(l % 5 == 0){
try {
this.saveIpDb(ip);
} catch (InterruptedException e) {
log.error("保存IP失败",e);
}
}
}
private void saveIpDb(String ip) throws InterruptedException {
IpRecord ipRecord = getByIpAndDate(ip, null);
if(ipRecord == null){
String lockKey = getLockKey(ip);
RLock lock = redissonClient.getLock(lockKey);
try {
boolean b = lock.tryLock(5, TimeUnit.SECONDS);
if(b){
ipRecord = getByIpAndDate(ip, null);
if(ipRecord == null){
IpRecord record = new IpRecord();
record.setIpAddr(ip);
long numb = getIp(ip);
record.setPathAddr("/api/auth/login");
record.setCountDate(LocalDate.now());
record.setNumber(numb);
record.setUpdateTime(LocalDateTime.now());
this.save(record);
}else{
long numb = getIp(ip);
IpRecord record = new IpRecord();
record.setId(ipRecord.getId());
record.setNumber(numb);
record.setUpdateTime(LocalDateTime.now());
this.updateById(record);
}
}
} finally {
lock.unlock();
}
}else{
long numb = getIp(ip);
IpRecord record = new IpRecord();
record.setId(ipRecord.getId());
record.setNumber(numb);
record.setUpdateTime(LocalDateTime.now());
this.updateById(record);
}
}
public static String KEY_LOCK = "cai:ipRecordLock:%s";
public static String getLockKey(String ip){
return String.format(KEY_LOCK, ip);
}
private IpRecord getByIpAndDate(String ip,LocalDate date){
if(date == null){
date = LocalDate.now();
}
return this.getOne(Wrappers.lambdaQuery(IpRecord.class).eq(IpRecord::getIpAddr, ip).eq(IpRecord::getCountDate, date).last("limit 1"));
}
public static String KEY = "cai:ipRecord:%s:%s";
private String getNow(){
return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
}
private String getKey(String ip){
return String.format(KEY, ip, getNow());
}
public long getIp(String ip){
String key = getKey(ip);
RAtomicLong atomicLong = redissonClient.getAtomicLong(key);
return atomicLong.get();
}
public long saveIp(String ip){
String key = getKey(ip);
RAtomicLong atomicLong = redissonClient.getAtomicLong(key);
long l = atomicLong.incrementAndGet();
return l;
}
} }

View File

@@ -3,9 +3,11 @@ package com.ruoyi.cai.service.impl;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.api.client.util.SecurityUtils;
import com.ruoyi.cai.domain.SmsVerify; import com.ruoyi.cai.domain.SmsVerify;
import com.ruoyi.cai.domain.User; import com.ruoyi.cai.domain.User;
import com.ruoyi.cai.domain.UserInfo; import com.ruoyi.cai.domain.UserInfo;
import com.ruoyi.cai.dto.commom.user.MinUser;
import com.ruoyi.cai.enums.CodeEnum; import com.ruoyi.cai.enums.CodeEnum;
import com.ruoyi.cai.enums.SystemConfigEnum; import com.ruoyi.cai.enums.SystemConfigEnum;
import com.ruoyi.cai.kit.AliSmsKit; import com.ruoyi.cai.kit.AliSmsKit;
@@ -17,6 +19,7 @@ import com.ruoyi.cai.service.SmsVerifyService;
import com.ruoyi.cai.service.UserInfoService; import com.ruoyi.cai.service.UserInfoService;
import com.ruoyi.cai.service.UserService; import com.ruoyi.cai.service.UserService;
import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.ServletUtils;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -81,12 +84,24 @@ public class SmsVerifyServiceImpl extends ServiceImpl<SmsVerifyMapper,SmsVerify>
throw new ServiceException("手机号已经被注册使用!"); throw new ServiceException("手机号已经被注册使用!");
} }
}else if(codeEnum == CodeEnum.RESET_PASSWORD){ }else if(codeEnum == CodeEnum.RESET_PASSWORD){
Long userId = LoginHelper.getUserId();
MinUser miniUser = userService.getMinUserById(userId);
if(miniUser == null || !mobile.equalsIgnoreCase(miniUser.getMobile())){
log.error("手机号与登录手机号不一致");
throw new ServiceException("手机号未注册!");
}
long count = userService.count(Wrappers.lambdaQuery(User.class) long count = userService.count(Wrappers.lambdaQuery(User.class)
.eq(User::getMobile, mobile)); .eq(User::getMobile, mobile));
if(count == 0){ if(count == 0){
throw new ServiceException("手机号未注册!"); throw new ServiceException("手机号未注册!");
} }
}else if(codeEnum == CodeEnum.RESET_ADOLESCENT){ }else if(codeEnum == CodeEnum.RESET_ADOLESCENT){
Long userId = LoginHelper.getUserId();
MinUser miniUser = userService.getMinUserById(userId);
if(miniUser == null || !mobile.equalsIgnoreCase(miniUser.getMobile())){
log.error("手机号与登录手机号不一致");
throw new ServiceException("手机号未注册!");
}
long count = userService.count(Wrappers.lambdaQuery(User.class) long count = userService.count(Wrappers.lambdaQuery(User.class)
.eq(User::getMobile, mobile)); .eq(User::getMobile, mobile));
if(count == 0){ if(count == 0){

View File

@@ -15,5 +15,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="updateTime" column="update_time"/> <result property="updateTime" column="update_time"/>
</resultMap> </resultMap>
<select id="pageAdmin" resultType="com.ruoyi.cai.dto.admin.vo.IpRecordAdminVO">
select t1.*,if(t2.id,1,0) as black
from cai_ip_record t1
left join cai_ip_black t2 on t1.ip_addr = t2.ip_addr and t2.enable_status = 1
where 1=1
<if test="bo.ipAddr != null and bo.ipAddr != ''">
and t1.ip_addr = #{bo.ipAddr}
</if>
<if test="bo.minNumber != null">
and t1.number > #{bo.minNumber}
</if>
order by t1.count_date desc, ip_addr
</select>
</mapper> </mapper>

View File

@@ -100,7 +100,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
limit 20 limit 20
</select> </select>
<select id="getMinUserById" resultType="com.ruoyi.cai.dto.commom.user.MinUser"> <select id="getMinUserById" resultType="com.ruoyi.cai.dto.commom.user.MinUser">
select id,gender select id,gender,mobile
from cai_user t1 from cai_user t1
where id = #{userId} where id = #{userId}
</select> </select>