增加验证码校验

This commit is contained in:
lijiaqi 2023-12-22 17:14:46 +08:00
parent 59621925cf
commit 373d9f2316
10 changed files with 739 additions and 15 deletions

View File

@ -50,7 +50,7 @@
<!-- Mybatis Plus多表连接 --> <!-- Mybatis Plus多表连接 -->
<mybatis.plus.join.version>1.2.4</mybatis.plus.join.version> <mybatis.plus.join.version>1.2.4</mybatis.plus.join.version>
<org.mapstruct.version>1.4.2.Final</org.mapstruct.version> <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
<redisson.version>3.20.0</redisson.version>
<app.build.name>${project.artifactId}-${project.version}</app.build.name> <app.build.name>${project.artifactId}-${project.version}</app.build.name>
<app.build.prod>target/build</app.build.prod> <app.build.prod>target/build</app.build.prod>
</properties> </properties>
@ -268,6 +268,12 @@
<scope>system</scope> <scope>system</scope>
<systemPath>${project.basedir}/lib/spire.doc.free-5.2.0.jar</systemPath> <systemPath>${project.basedir}/lib/spire.doc.free-5.2.0.jar</systemPath>
</dependency> </dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>${redisson.version}</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -1,6 +1,10 @@
package com.ydool.common.config; package com.ydool.common.config;
import com.ydool.common.interceptor.SqlLogInterceptor; import com.ydool.common.interceptor.SqlLogInterceptor;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -10,4 +14,17 @@ public class LogConfig {
public SqlLogInterceptor sqlLogInterceptor() { public SqlLogInterceptor sqlLogInterceptor() {
return new SqlLogInterceptor(); return new SqlLogInterceptor();
} }
@Value("${redis:address}")
private String address;
@Value("${redis:password}")
private String password;
@Bean
public RedissonClient redissonClient(){
Config config = new Config();
config.useSingleServer().setAddress(address).setPassword(password);
return Redisson.create();
}
} }

View File

@ -0,0 +1,20 @@
package com.ydool.common.constant;
public interface RedissonConstant {
/**
* 验证码
*/
String CAPTCHA_CODE = "lc:::oa:::captcha:::code";
/**
* ip请求验证码接口次数
*/
String CAPTCHA_CODE_COUNT = "lc:::oa:::captcha:::code:::count";
/**
* ip连续登录失败次数
*/
String LOGIN_FAIL_COUNT = "lc:::oa:::login:::fail:::count";
/**
* ip黑名单
*/
String IP_BLACK_LIST = "lc:::oa:::ip:::black:::list";
}

View File

@ -0,0 +1,498 @@
package com.ydool.common.utils;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.redisson.client.codec.StringCodec;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
/**
* @author liuhaoze
* @date 2023/7/17 11:00
*/
@Slf4j
@Component
@ConditionalOnProperty(name = "redis.redisson.enabled", havingValue = "true")
public class RedisUtils {
@Resource
private RedissonClient redissonClient;
//----------------------------------------------------------------
/**
* 获取 字符串 RSetCache
*
* @param name 缓存名称
* @return RSetCache
*/
public RSetCache<String> getSetCache(String name) {
RSetCache<String> setCache = redissonClient.getSetCache(name, StringCodec.INSTANCE);
return setCache;
}
/**
* 新增 RSetCache 数据
*
* @param name 缓存名称
* @param value
* @param size 缓存时间 单位:s
* @param timeUnit 缓存时间单位
* @return 是否新增成功
*/
public boolean addSetCache(String name, String value, Integer size, TimeUnit timeUnit) {
if (ObjectUtil.isNotNull(size) && ObjectUtil.isNotNull(timeUnit)) {
return this.getSetCache(name).add(value, size, timeUnit);
}
return this.getSetCache(name).add(value);
}
/**
* 新增 RSetCache 数据
*
* @param name 缓存名称
* @param value
* @return 是否新增成功
*/
public boolean addSetCache(String name, String value) {
return this.getSetCache(name).add(value);
}
/**
* 删除 RSetCache 数据
*
* @param name 缓存名称
* @param value 需要移除的值
* @return 是否移除成功
*/
public boolean removeSetCache(String name, String value) {
return this.getSetCache(name).remove(value);
}
/**
* 验证RSetCache里是否包含
*
* @param name 缓存name
* @param value 需要验证的值
* @return 是否包含
*/
public boolean setCacheContains(String name, String value) {
return this.getSetCache(name).contains(value);
}
/**
* 刷新RSetCache的某个参数的缓存时间
*
* @param name 缓存name
* @param value 需要刷新缓存时间的值
* @param size 缓存时间
* @param timeUnit 缓存时间单位
* @return
*/
public boolean setCacheRefresh(String name, String value, Integer size, TimeUnit timeUnit) {
this.removeSetCache(name, value);
return this.addSetCache(name, value, size, timeUnit);
}
//----------------------------------------------------------------
//----------------------------------------------------------------
/**
* 获取 RMapCache
*
* @param name 缓存名
* @return RMapCache
*/
public RMapCache<String, Object> getMapCache(String name) {
return redissonClient.getMapCache(name, StringCodec.INSTANCE);
}
/**
* 获取 RMapCache
*
* @param name 缓存名
* @param key
* @return 返回 返回结果
*/
public Object getMapCache(String name, String key) {
return getMapCache(name).get(key);
}
/**
* 新增 RMapCache 数据
*
* @param name 缓存名称
* @param key
* @param value
* @param size 缓存时间 单位:s
* @param timeUnit 缓存时间单位
* @return 返回 返回结果
*/
public Object putMapCache(String name, String key, Object value, Integer size, TimeUnit timeUnit) {
if (ObjectUtil.isNotNull(size) && ObjectUtil.isNotNull(timeUnit)) {
return getMapCache(name).put(key, value, size, timeUnit);
}
return getMapCache(name).put(key, value);
}
/**
* 新增 RMapCache 数据
*
* @param name 缓存名称
* @param key
* @param value
* @return 返回 返回结果
*/
public Object putMapCache(String name, String key, Object value) {
return getMapCache(name).put(key, value);
}
/**
* 删除 RMapCache 数据
*
* @param name 缓存名称
* @param key
* @return 返回 返回结果
*/
public Object removeMapCache(String name, String key) {
return getMapCache(name).remove(key);
}
/**
* 更新 RMapCache 数据
*
* @param name 缓存名称
* @param key
* @param value
* @param size 缓存时间 单位:s
* @param timeUnit 缓存时间单位
* @return 返回 返回结果
*/
public Object mapCacheRefresh(String name, String key, Object value, Integer size, TimeUnit timeUnit) {
this.removeMapCache(name, key);
return this.putMapCache(name, key, value, size, timeUnit);
}
//----------------------------------------------------------------
//----------------------------------------------------------------
/**
* 获取 字符串 RSet
*
* @param name 缓存名称
* @return RSet
*/
public RSet<String> getSet(String name) {
RSet<String> set = redissonClient.getSet(name, StringCodec.INSTANCE);
return set;
}
/**
* 新增 RSet 数据
*
* @param name 缓存名称
* @param value
* @return 是否新增成功
*/
public boolean addSet(String name, String value) {
return this.getSet(name).add(value);
}
/**
* 新增 RSet 数据
*
* @param name 缓存名称
* @param values
* @return 是否新增成功
*/
public boolean addSet(String name, List<String> values) {
return this.getSet(name).addAll(values);
}
/**
* 删除 RSet 数据
*
* @param name 缓存名称
* @param value 需要移除的值
* @return 是否移除成功
*/
public boolean removeSet(String name, String value) {
return this.getSet(name).remove(value);
}
/**
* 验证是否包含
*
* @param name 缓存name
* @param value 需要验证的值
* @return 是否包含
*/
public boolean contains(String name, String value) {
return this.getSet(name).contains(value);
}
//----------------------------------------------------------------
//----------------------------------------------------------------
/**
* 获取 RMap
*
* @param name 缓存名
* @return RMap
*/
public RMap<String, Object> getMap(String name) {
RMap<String, Object> map = redissonClient.getMap(name, StringCodec.INSTANCE);
return map;
}
/**
* 获取 RMap
*
* @param name 缓存名
* @param key
* @return 返回 返回结果
*/
public Object getMapObject(String name, String key) {
return getMap(name).get(key);
}
/**
* 新增 RMap 数据
*
* @param name 名称
* @param key
* @param value
* @return 返回 返回结果
*/
public Object putMap(String name, String key, Object value) {
return this.getMap(name).put(key, value);
}
/**
* 新增 RMap 数据
*
* @param name 名称
* @param map
*/
public void putAllMap(String name, Map<String, Object> map) {
this.getMap(name).putAll(map);
}
/**
* 删除 RMap 数据
*
* @param name 名称
* @param key
* @return 返回 返回结果
*/
public Object removeMap(String name, String key) {
return this.getMap(name).remove(key);
}
//----------------------------------------------------------------
/**
* SET缓存
* @param name 缓存KEY
* @param value 缓存值
*/
public void set(String name, Object value) {
set(name, value, null);
}
/**
* SET缓存
* @param name 缓存KEY
* @param value 缓存值
* @param seconds 缓存秒数
*/
public void set(String name, Object value, Integer seconds) {
RBucket<Object> bucket = redissonClient.getBucket(name);
bucket.set(value);
if (seconds != null) {
bucket.expire(seconds, TimeUnit.SECONDS);
}
}
/**
* GET缓存 String
* @param name 缓存NAME
* @return 缓存对应的值 不存在返回NULL
*/
public String getStr(String name) {
RBucket<String> bucket = redissonClient.getBucket(name);
if(bucket.isExists()) {
return bucket.get();
}
return null;
}
/**
* GET缓存 Integer
* @param name 缓存KEY
* @return 缓存对应的值 不存在返回NULL
*/
public Integer getInt(String name) {
RBucket<Integer> bucket = redissonClient.getBucket(name);
if(bucket.isExists()) {
return bucket.get();
}
return null;
}
/**
* 从缓存中获取数据如果缓存中不存在则从执行指定的方法获取数据并将得到的数据同步到缓存
*
* @param name 缓存的键
* @param key 数据名称
* @param f 获取数据的方法
* @return 数据对象
*/
public String getFromCacheStr(String name, String key, Function<String, String> f) {
return this.getFromCacheStr(name, key, f, null);
}
/**
* 从缓存中获取数据如果缓存中不存在则从执行指定的方法获取数据并将得到的数据同步到缓存
*
* @param name 缓存的名称
* @param key 数据key
* @param f 获取数据的方法
* @param seconds 过期秒数
* @return 数据对象
*/
public String getFromCacheStr(String name, String key, Function<String, String> f, Integer seconds) {
String m;
RBucket<String> bucket = redissonClient.getBucket(name);
if (!bucket.isExists()) {
m = f.apply(key);
if (m != null) {
bucket.set(m);
if (seconds != null) {
bucket.expire(seconds, TimeUnit.SECONDS);
}
}
} else {
m = bucket.get();
}
return m;
}
/**
* 从缓存中获取数据如果缓存中不存在则从执行指定的方法获取数据并将得到的数据同步到缓存
*
* @param name 缓存的键
* @param id 数据Id
* @param f 获取数据的方法
* @param clazz 数据对象类型
* @return 数据对象
*/
public <M> M getFromCache(String name, Serializable id, Function<Serializable, M> f, Class<M> clazz) {
return this.getFromCache(name, id, f, clazz, null);
}
/**
* 从缓存中获取数据如果缓存中不存在则从执行指定的方法获取数据并将得到的数据同步到缓存
*
* @param name 缓存的键
* @param filter mybatis plus的过滤对象
* @param f 获取数据的方法
* @param clazz 数据对象类型
* @return 数据对象
*/
public <N> N getFromCache(
String name, LambdaQueryWrapper<N> filter, Function<LambdaQueryWrapper<N>, N> f, Class<N> clazz) {
N m;
RBucket<String> bucket = redissonClient.getBucket(name);
if (!bucket.isExists()) {
m = f.apply(filter);
if (m != null) {
bucket.set(JSONUtil.toJsonStr(m));
}
} else {
m = JSONUtil.toBean(bucket.get(), clazz);
}
return m;
}
/**
* 从缓存中获取数据如果缓存中不存在则从执行指定的方法获取数据并将得到的数据同步到缓存
*
* @param name 缓存的键
* @param filter 过滤对象
* @param f 获取数据的方法
* @param clazz 数据对象类型
* @return 数据对象
*/
public <M, N> N getFromCache(String name, M filter, Function<M, N> f, Class<N> clazz) {
N m;
RBucket<String> bucket = redissonClient.getBucket(name);
if (!bucket.isExists()) {
m = f.apply(filter);
if (m != null) {
bucket.set(JSONUtil.toJsonStr(m));
}
} else {
m = JSONUtil.toBean(bucket.get(), clazz);
}
return m;
}
/**
* 从缓存中获取数据如果缓存中不存在则从执行指定的方法获取数据并将得到的数据同步到缓存
*
* @param name 缓存的键
* @param id 数据Id
* @param f 获取数据的方法
* @param clazz 数据对象类型
* @param seconds 过期秒数
* @return 数据对象
*/
public <M> M getFromCache(
String name, Serializable id, Function<Serializable, M> f, Class<M> clazz, Integer seconds) {
M m;
RBucket<String> bucket = redissonClient.getBucket(name);
if (!bucket.isExists()) {
m = f.apply(id);
if (m != null) {
bucket.set(JSONUtil.toJsonStr(m));
if (seconds != null) {
bucket.expire(seconds, TimeUnit.SECONDS);
}
}
} else {
m = JSONUtil.toBean(bucket.get(), clazz);
}
return m;
}
/**
* 移除指定缓存
*
* @param name 缓存名
*/
public void evictFormCache(String name) {
redissonClient.getBucket(name).delete();
}
}

View File

@ -14,7 +14,7 @@ public class SaTokenConfig implements WebMvcConfigurer {
// 注册 Sa-Token 拦截器校验规则为 StpUtil.checkLogin() 登录校验 // 注册 Sa-Token 拦截器校验规则为 StpUtil.checkLogin() 登录校验
registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin())) registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
.addPathPatterns(UrlConstant.API + "/**") .addPathPatterns(UrlConstant.API + "/**")
.excludePathPatterns(UrlConstant.AUTH+"/login",UrlConstant.PERSON+"/getPersonnelRetire"); .excludePathPatterns(UrlConstant.AUTH+"/login",UrlConstant.PERSON+"/getPersonnelRetire",UrlConstant.AUTH+"/image");
} }
@Override @Override

View File

@ -1,5 +1,7 @@
package com.ydool.system.controller; package com.ydool.system.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.core.lang.Dict;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport; import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.ydool.common.constant.UrlConstant; import com.ydool.common.constant.UrlConstant;
@ -34,7 +36,7 @@ public class AuthController {
@ApiOperation("登录") @ApiOperation("登录")
@ApiOperationSupport(order = 1) @ApiOperationSupport(order = 1)
public AjaxResult login(@RequestBody @Validated AuthRequest authRequest) { public AjaxResult login(@RequestBody @Validated AuthRequest authRequest) {
return authService.login(authRequest.getUserName(), authRequest.getPassword()); return authService.login(authRequest.getUserName(), authRequest.getPassword(),authRequest.getCaptchaId(),authRequest.getCaptcha());
} }
/** /**
@ -88,5 +90,17 @@ public class AuthController {
return authService.editSign(newSignRequest.getSign()); return authService.editSign(newSignRequest.getSign());
} }
/**
* 获取图片验证码接口
*
* @return
*/
@GetMapping("/image")
@ApiOperation("获取图片验证码接口")
public AjaxResult captcha() {
return authService.captcha();
}
} }

View File

@ -16,4 +16,12 @@ public class AuthRequest {
@ApiModelProperty(value = "密码", required = true) @ApiModelProperty(value = "密码", required = true)
private String password; private String password;
@NotBlank(message = "请输入验证码")
@ApiModelProperty(value = "验证码", required = true)
private String captcha;
@NotBlank(message = "请输入验证码ID")
@ApiModelProperty(value = "验证码ID", required = true)
private String captchaId;
} }

View File

@ -9,7 +9,7 @@ public interface IAuthService {
* @param password 密码 * @param password 密码
* @return * @return
*/ */
AjaxResult login(String userName, String password); AjaxResult login(String userName, String password, String captchaId, String captcha);
/** /**
* 登出 * 登出
@ -38,4 +38,6 @@ public interface IAuthService {
* @return * @return
*/ */
AjaxResult editSign(String sign); AjaxResult editSign(String sign);
AjaxResult captcha();
} }

View File

@ -2,20 +2,22 @@ package com.ydool.system.service.impl;
import cn.dev33.satoken.stp.SaTokenInfo; import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Dict; import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
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 com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ydool.common.base.BaseService; import com.ydool.common.base.BaseService;
import com.ydool.common.cache.ConfigCache; import com.ydool.common.cache.ConfigCache;
import com.ydool.common.constant.RedissonConstant;
import com.ydool.common.constant.UrlConstant; import com.ydool.common.constant.UrlConstant;
import com.ydool.common.data.dto.AjaxResult; import com.ydool.common.data.dto.AjaxResult;
import com.ydool.common.utils.CacheUtil; import com.ydool.common.utils.*;
import com.ydool.common.utils.HttpServletUtil;
import com.ydool.common.utils.PasswordUtil;
import com.ydool.common.utils.RsaUtil;
import com.ydool.system.entity.Dept; import com.ydool.system.entity.Dept;
import com.ydool.system.entity.Menu; import com.ydool.system.entity.Menu;
import com.ydool.system.entity.Role; import com.ydool.system.entity.Role;
@ -33,6 +35,7 @@ import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@ -46,6 +49,9 @@ public class AuthServiceImpl extends BaseService<UserMapper, User> implements IA
@Autowired @Autowired
private DeptServiceImpl deptService; private DeptServiceImpl deptService;
@Autowired
private RedisUtils redisUtils;
/** /**
* 登录接口 * 登录接口
@ -56,7 +62,35 @@ public class AuthServiceImpl extends BaseService<UserMapper, User> implements IA
*/ */
@SneakyThrows @SneakyThrows
@Override @Override
public AjaxResult login(String userName, String password) { public AjaxResult login(String userName, String password, String captchaId, String captcha) {
//获取IP
String ip = HttpServletUtil.getRemoteAddress();
//判断是否在黑名单中
boolean ipBlack = redisUtils.setCacheContains(RedissonConstant.IP_BLACK_LIST, ip);
if (ipBlack) {
//如果在黑名单中则20分钟后再尝试
redisUtils.setCacheRefresh(RedissonConstant.IP_BLACK_LIST, ip, 20, TimeUnit.MINUTES);
return AjaxResult.fail("您的IP地址在黑名单中请20分钟后再尝试");
}
//判断验证码是否正确
String captchaCache = (String) redisUtils.getMapCache(RedissonConstant.CAPTCHA_CODE, captchaId);
if (!captcha.equalsIgnoreCase(captchaCache)) {
//获取IP失败次数
Integer failCount = (Integer) redisUtils.getMapCache(RedissonConstant.LOGIN_FAIL_COUNT, ip);
if (ObjectUtil.isNull(failCount)) {
failCount = 1;
} else {
failCount++;
}
if (failCount >= 5) {
redisUtils.putMapCache(RedissonConstant.LOGIN_FAIL_COUNT, ip, failCount, 20, TimeUnit.MINUTES);
redisUtils.setCacheRefresh(RedissonConstant.IP_BLACK_LIST, ip, 20, TimeUnit.MINUTES);
}
return AjaxResult.fail("验证码错误");
}
//清空验证码缓存以及验证码请求次数缓存
redisUtils.removeMapCache(RedissonConstant.CAPTCHA_CODE, captchaId);
redisUtils.removeMapCache(RedissonConstant.CAPTCHA_CODE_COUNT, ip);
//用户名密码按照UTF-8编码解密 //用户名密码按照UTF-8编码解密
userName = URLDecoder.decode(userName, StandardCharsets.UTF_8.name()); userName = URLDecoder.decode(userName, StandardCharsets.UTF_8.name());
password = URLDecoder.decode(password, StandardCharsets.UTF_8.name()); password = URLDecoder.decode(password, StandardCharsets.UTF_8.name());
@ -65,19 +99,83 @@ public class AuthServiceImpl extends BaseService<UserMapper, User> implements IA
password = RsaUtil.decrypt(password, UrlConstant.APP_PRIVATE_KEY); password = RsaUtil.decrypt(password, UrlConstant.APP_PRIVATE_KEY);
//根据用户名查询账号是否存在 //根据用户名查询账号是否存在
User loginUser = getOne(new QueryWrapper<User>().lambda().eq(User::getLoginName, userName)); User loginUser = getOne(new QueryWrapper<User>().lambda().eq(User::getLoginName, userName));
if (ObjectUtil.isNull(loginUser)) return AjaxResult.fail("账号或者密码错误"); if (ObjectUtil.isNull(loginUser)) {
//获取IP失败次数
Integer failCount = (Integer) redisUtils.getMapCache(RedissonConstant.LOGIN_FAIL_COUNT, ip);
if (ObjectUtil.isNull(failCount)) {
failCount = 1;
} else {
failCount++;
}
if (failCount >= 5) {
redisUtils.putMapCache(RedissonConstant.LOGIN_FAIL_COUNT, ip, failCount, 20, TimeUnit.MINUTES);
redisUtils.setCacheRefresh(RedissonConstant.IP_BLACK_LIST, ip, 20, TimeUnit.MINUTES);
}
return AjaxResult.fail("账号或者密码错误");
}
//加密密码 //加密密码
String loginPassword = PasswordUtil.password(loginUser.getSalt(), password); String loginPassword = PasswordUtil.password(loginUser.getSalt(), password);
//与数据库的密码进行匹配 //与数据库的密码进行匹配
if (!loginPassword.equals(loginUser.getPassword())) return AjaxResult.fail("账号或者密码错误"); if (!loginPassword.equals(loginUser.getPassword())) {
//获取IP失败次数
Integer failCount = (Integer) redisUtils.getMapCache(RedissonConstant.LOGIN_FAIL_COUNT, ip);
if (ObjectUtil.isNull(failCount)) {
failCount = 1;
} else {
failCount++;
}
if (failCount >= 5) {
redisUtils.putMapCache(RedissonConstant.LOGIN_FAIL_COUNT, ip, failCount, 20, TimeUnit.MINUTES);
redisUtils.setCacheRefresh(RedissonConstant.IP_BLACK_LIST, ip, 20, TimeUnit.MINUTES);
}
return AjaxResult.fail("账号或者密码错误");
}
//判断账号是否停用 //判断账号是否停用
if (!loginUser.getStatus()) return AjaxResult.fail("当前账号已被停用,请联系管理员"); if (!loginUser.getStatus()) {
//获取IP失败次数
Integer failCount = (Integer) redisUtils.getMapCache(RedissonConstant.LOGIN_FAIL_COUNT, ip);
if (ObjectUtil.isNull(failCount)) {
failCount = 1;
} else {
failCount++;
}
if (failCount >= 5) {
redisUtils.putMapCache(RedissonConstant.LOGIN_FAIL_COUNT, ip, failCount, 20, TimeUnit.MINUTES);
redisUtils.setCacheRefresh(RedissonConstant.IP_BLACK_LIST, ip, 20, TimeUnit.MINUTES);
}
return AjaxResult.fail("当前账号已被停用,请联系管理员");
}
//判断账号角色 //判断账号角色
if (StrUtil.isBlank(loginUser.getRoles())) return AjaxResult.fail("该用户没有对应的角色,无法登陆系统"); if (StrUtil.isBlank(loginUser.getRoles())) {
//获取IP失败次数
Integer failCount = (Integer) redisUtils.getMapCache(RedissonConstant.LOGIN_FAIL_COUNT, ip);
if (ObjectUtil.isNull(failCount)) {
failCount = 1;
} else {
failCount++;
}
if (failCount >= 5) {
redisUtils.putMapCache(RedissonConstant.LOGIN_FAIL_COUNT, ip, failCount, 20, TimeUnit.MINUTES);
redisUtils.setCacheRefresh(RedissonConstant.IP_BLACK_LIST, ip, 20, TimeUnit.MINUTES);
}
return AjaxResult.fail("该用户没有对应的角色,无法登陆系统");
}
List<Role> roleList = userService.roleListByUser(loginUser.getId()); List<Role> roleList = userService.roleListByUser(loginUser.getId());
if (CollUtil.isEmpty(roleList)) return AjaxResult.fail("该用户没有对应的角色或角色已被删除或禁用,无法登陆系统"); if (CollUtil.isEmpty(roleList)) { //获取IP失败次数
Integer failCount = (Integer) redisUtils.getMapCache(RedissonConstant.LOGIN_FAIL_COUNT, ip);
if (ObjectUtil.isNull(failCount)) {
failCount = 1;
} else {
failCount++;
}
if (failCount >= 5) {
redisUtils.putMapCache(RedissonConstant.LOGIN_FAIL_COUNT, ip, failCount, 20, TimeUnit.MINUTES);
redisUtils.setCacheRefresh(RedissonConstant.IP_BLACK_LIST, ip, 20, TimeUnit.MINUTES);
}
return AjaxResult.fail("该用户没有对应的角色或角色已被删除或禁用,无法登陆系统");
}
loginUser.setLoginDate(LocalDateTime.now()); loginUser.setLoginDate(LocalDateTime.now());
@ -87,8 +185,10 @@ public class AuthServiceImpl extends BaseService<UserMapper, User> implements IA
StpUtil.login(loginUser.getId()); StpUtil.login(loginUser.getId());
CacheUtil.put(ConfigCache.SCHEDULED_CODE_PERSONNEL_RETIRE, ConfigCache.SCHEDULED_CODE_PERSONNEL_RETIRE, "scheduled", false); CacheUtil.put(ConfigCache.SCHEDULED_CODE_PERSONNEL_RETIRE, ConfigCache.SCHEDULED_CODE_PERSONNEL_RETIRE, "scheduled", false);
boolean flag = updateById(loginUser); boolean flag = updateById(loginUser);
if (flag) { if (flag) {
//清空ip黑名单和登录失败次数缓存
redisUtils.removeMapCache(RedissonConstant.LOGIN_FAIL_COUNT, ip);
redisUtils.removeSetCache(RedissonConstant.IP_BLACK_LIST, ip);
// 第2步获取 Token 相关参数 // 第2步获取 Token 相关参数
SaTokenInfo tokenInfo = StpUtil.getTokenInfo(); SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
Dict result = Dict.create(); Dict result = Dict.create();
@ -190,5 +290,31 @@ public class AuthServiceImpl extends BaseService<UserMapper, User> implements IA
return flag ? AjaxResult.ok().msg("修改电子签名成功") : AjaxResult.fail("修改电子签名失败"); return flag ? AjaxResult.ok().msg("修改电子签名成功") : AjaxResult.fail("修改电子签名失败");
} }
@Override
public AjaxResult captcha() {
//获取请求IP
String ip = HttpServletUtil.getRemoteAddress();
Integer count = (Integer) redisUtils.getMapCache(RedissonConstant.CAPTCHA_CODE_COUNT, ip);
if (count != null && count > 5) {
redisUtils.putMapCache(RedissonConstant.CAPTCHA_CODE_COUNT, ip, count + 1, 1, TimeUnit.MINUTES);
return AjaxResult.fail("验证码获取频繁,请稍后再试");
}
// 自定义纯数字的验证码随机4位数字可重复
RandomGenerator randomGenerator = new RandomGenerator("0123456789", 4);
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100, 4, 30);
lineCaptcha.setGenerator(randomGenerator);
// 重新生成code
lineCaptcha.createCode();
String uuid = IdUtil.randomUUID();
String code = lineCaptcha.getCode();
String imageBase64 = lineCaptcha.getImageBase64();
Dict dict = new Dict();
dict.set("key", uuid);
dict.set("value", "data:image/jpeg;base64," + imageBase64);
redisUtils.putMapCache(RedissonConstant.CAPTCHA_CODE, uuid, code, 1, TimeUnit.MINUTES);
redisUtils.putMapCache(RedissonConstant.CAPTCHA_CODE_COUNT, ip, count == null ? 1 : count + 1, 1, TimeUnit.MINUTES);
return AjaxResult.ok().data(dict);
}
} }

View File

@ -119,3 +119,36 @@ sa-token:
# token前缀 # token前缀
token-prefix: Bearer token-prefix: Bearer
redis:
# redisson的配置。每个服务可以自己的配置文件中覆盖此选项。
redisson:
# 如果该值为false系统将不会创建RedissionClient的bean。
enabled: true
# mode的可用值为single/cluster/sentinel/master-slave
mode: single
# single: 单机模式
# address: redis://localhost:6379
# cluster: 集群模式
# 每个节点逗号分隔同时每个节点前必须以redis://开头。
# address: redis://localhost:6379,redis://localhost:6378,...
# sentinel:
# 每个节点逗号分隔同时每个节点前必须以redis://开头。
# address: redis://localhost:6379,redis://localhost:6378,...
# master-slave:
# 每个节点逗号分隔第一个为主节点其余为从节点。同时每个节点前必须以redis://开头。
# address: redis://localhost:6379,redis://localhost:6378,...
address: redis://localhost:6379
# 链接超时,单位毫秒。
timeout: 6000
# 单位毫秒。分布式锁的超时检测时长。
# 如果一次锁内操作超该毫秒数或在释放锁之前异常退出Redis会在该时长之后主动删除该锁使用的key。
lockWatchdogTimeout: 60000
# redis 密码,空可以不填。
password:
pool:
# 连接池数量。
poolSize: 20
# 连接池中最小空闲数量。
minIdle: 5