feat: 整合滑动图像验证码完成

This commit is contained in:
landaiqing
2024-06-05 01:38:51 +08:00
parent 5be45564cf
commit 213d9bc607
8 changed files with 194 additions and 68 deletions

View File

@@ -41,6 +41,6 @@ public class GlobalConfig extends WebMvcConfigurationSupport {
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**").excludePathPatterns("/oauth/**","/system/**","/user/**","/ReactRotateCaptcha/**","/sms/**");
.addPathPatterns("/**").excludePathPatterns("/oauth/**","/system/**","/auth/**","/ReactRotateCaptcha/**","/sms/sendByTemplate");
}
}

View File

@@ -1,6 +1,8 @@
package com.schisandra.auth.application.controller;
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.lang.generator.UUIDGenerator;
import com.schisandra.auth.application.dto.SchisandraCaptchaDTO;
import com.schisandra.auth.common.entity.CaptchaResult;
import com.schisandra.auth.common.redis.RedisUtil;
@@ -28,10 +30,8 @@ public class ReactRotateCaptchaController {
@Resource
private RedisUtil redisUtil;
public final String authRotateCaptchaPrefix = "auth.RotateCaptcha";
public final String authRotateCaptchaPrefix = "auth.rotate.captcha";
@Value("${cipher.salt}")
private String salt;
/**
* @description: 获取图片
@@ -50,7 +50,7 @@ public class ReactRotateCaptchaController {
double randomNumber = random.nextInt(280) + 40;
InputStream inputStream = ReactRotateCaptchaController.class.getClassLoader().getResourceAsStream("image/test1.jpg");
BufferedImage image = ImageIO.read(inputStream);
String token = SaSecureUtil.md5BySalt(String.valueOf(randomNumber), salt);
String token = UUID.randomUUID().toString();
String prefix = redisUtil.buildKey(authRotateCaptchaPrefix, token);
redisUtil.setNx(prefix, String.valueOf(randomNumber), 60L, TimeUnit.SECONDS);
BufferedImage image1 = rotateImageUtils.rotateImage(image, randomNumber, Color.black);

View File

@@ -5,6 +5,8 @@ import com.alibaba.fastjson.JSON;
import com.google.common.base.Preconditions;
import com.schisandra.auth.application.convert.SchisandraAuthUserDTOConverter;
import com.schisandra.auth.application.dto.SchisandraAuthUserDTO;
import com.schisandra.auth.application.utils.CheckRouteCaptcha;
import com.schisandra.auth.common.entity.CaptchaResult;
import com.schisandra.auth.common.entity.Result;
import com.schisandra.auth.common.redis.RedisUtil;
import com.schisandra.auth.domain.bo.SchisandraAuthUserBO;
@@ -38,6 +40,9 @@ public class SchisandraAuthUserController {
private final String AUTH_PHONE_PREFIX = "auth.phone";
@Resource
CheckRouteCaptcha checkRouteCaptcha;
/**
* @description: 注册
* @param: [schisandraAuthUserDTO]
@@ -46,7 +51,7 @@ public class SchisandraAuthUserController {
* @date: 2024/5/26 17:23
*/
@PostMapping("register")
public Result register(@RequestBody SchisandraAuthUserDTO schisandraAuthUserDTO) {
public CaptchaResult register(@RequestBody SchisandraAuthUserDTO schisandraAuthUserDTO) {
if (log.isInfoEnabled()) {
log.info("UserController.register.dto:{}", JSON.toJSONString(schisandraAuthUserDTO));
}
@@ -54,32 +59,39 @@ public class SchisandraAuthUserController {
log.error("UserController.register.phone is null");
return null;
}
SchisandraAuthUser schisandraAuthUser = schisandraAuthUserDomainService.queryByPhone(schisandraAuthUserDTO.getPhone());
if (ObjectUtils.isNotEmpty(schisandraAuthUser)) {
return Result.fail("该手机号已注册");
} else {
String key = redisUtil.buildKey(AUTH_PHONE_PREFIX, schisandraAuthUserDTO.getPhone());
if (redisUtil.exist(key)) {
if (!Objects.equals(redisUtil.get(key), schisandraAuthUserDTO.getActiveCode())) {
return Result.fail("验证码错误,请重新验证");
}
try {
SchisandraAuthUserBO authUserBO = SchisandraAuthUserDTOConverter.INSTANCE.convertDTOToBO(schisandraAuthUserDTO);
if (schisandraAuthUserDomainService.register(authUserBO)) {
return Result.ok("注册成功");
} else {
return Result.fail("注册失败");
}
} catch (Exception e) {
log.error("UserController.register.error:{}", e.getMessage(), e);
return Result.fail("注册用户失败");
}
String token = schisandraAuthUserDTO.getToken();
Double deg = schisandraAuthUserDTO.getDeg();
CaptchaResult captchaResult = checkRouteCaptcha.checkCaptcha(token, deg);
if (captchaResult.getCode() == 0) {
SchisandraAuthUser schisandraAuthUser = schisandraAuthUserDomainService.queryByPhone(schisandraAuthUserDTO.getPhone());
if (ObjectUtils.isNotEmpty(schisandraAuthUser)) {
return CaptchaResult.failWithCode("该手机号已注册");
} else {
return Result.fail("验证码错误,请重新验证");
String key = redisUtil.buildKey(AUTH_PHONE_PREFIX, schisandraAuthUserDTO.getPhone());
if (redisUtil.exist(key)) {
if (!Objects.equals(redisUtil.get(key), schisandraAuthUserDTO.getActiveCode())) {
return CaptchaResult.failWithCode("验证码错误,请重新验证");
}
try {
SchisandraAuthUserBO authUserBO = SchisandraAuthUserDTOConverter.INSTANCE.convertDTOToBO(schisandraAuthUserDTO);
if (schisandraAuthUserDomainService.register(authUserBO)) {
return CaptchaResult.ok("注册成功");
} else {
return CaptchaResult.failWithCode("注册失败");
}
} catch (Exception e) {
log.error("UserController.register.error:{}", e.getMessage(), e);
return CaptchaResult.failWithCode("注册用户失败");
}
} else {
return CaptchaResult.failWithCode("验证码错误,请重新验证");
}
}
}else{
return CaptchaResult.fail();
}
}
/**
* @description: 用户登录
* @param: [schisandraAuthUserDTO]
@@ -88,25 +100,37 @@ public class SchisandraAuthUserController {
* @date: 2024/5/31 1:19
*/
@PostMapping("login")
public Result login(@RequestBody SchisandraAuthUserDTO schisandraAuthUserDTO) {
HashMap<String, Object> map = new HashMap<>();
SchisandraAuthUserBO schisandraAuthUserBO=SchisandraAuthUserDTOConverter.INSTANCE.convertDTOToBO(schisandraAuthUserDTO);
SchisandraAuthUserDTO result=SchisandraAuthUserDTOConverter.INSTANCE.convertBOToDTO(schisandraAuthUserDomainService.login(schisandraAuthUserBO));
map.put("user",result);
if(result!=null){
if (StpUtil.isLogin(result.getId())){
StpUtil.logout(result.getId());
StpUtil.login(result.getId());
String token=StpUtil.getTokenValueByLoginId(result.getId());
map.put("token",token);
return Result.ok(map);
}else {
StpUtil.login(result.getId());
return Result.ok(result);
}
}else{
return Result.fail("用户名或密码错误");}
public CaptchaResult login(@RequestBody SchisandraAuthUserDTO schisandraAuthUserDTO) {
if (schisandraAuthUserDTO.getDeg() == null && schisandraAuthUserDTO.getToken() == null) {
return CaptchaResult.fail("验证失败!");
}
String token = schisandraAuthUserDTO.getToken();
Double deg = schisandraAuthUserDTO.getDeg();
CaptchaResult captchaResult = checkRouteCaptcha.checkCaptcha(token, deg);
if (captchaResult.getCode() == 0) {
HashMap<String, Object> map = new HashMap<>();
SchisandraAuthUserBO schisandraAuthUserBO = SchisandraAuthUserDTOConverter.INSTANCE.convertDTOToBO(schisandraAuthUserDTO);
SchisandraAuthUserBO login = schisandraAuthUserDomainService.login(schisandraAuthUserBO);
SchisandraAuthUserDTO result = SchisandraAuthUserDTOConverter.INSTANCE.convertBOToDTO(login);
map.put("user", result);
if (login != null) {
if (StpUtil.isLogin(result.getId())) {
StpUtil.logout(result.getId());
StpUtil.login(result.getId());
String userToken = StpUtil.getTokenValueByLoginId(result.getId());
map.put("token", userToken);
return CaptchaResult.ok(map);
} else {
StpUtil.login(result.getId());
return CaptchaResult.ok(result);
}
} else {
return CaptchaResult.failWithCode();
}
} else {
return CaptchaResult.fail();
}
}
/**

View File

@@ -2,14 +2,18 @@ package com.schisandra.auth.application.controller;
import com.schisandra.auth.application.context.SmsConfigContext;
import com.schisandra.auth.common.entity.Result;
import com.schisandra.auth.application.dto.SchisandraCaptchaDTO;
import com.schisandra.auth.application.utils.CheckRouteCaptcha;
import com.schisandra.auth.common.entity.CaptchaResult;
import com.schisandra.auth.common.redis.RedisUtil;
import com.schisandra.auth.common.utils.SmsCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.comm.utils.SmsUtils;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@@ -25,6 +29,9 @@ public class SchisandraSmsController {
private final String AUTH_PHONE_PREFIX = "auth.phone";
private final String SMS_CONFIG_PREFIX = "sys.config";
@Resource
CheckRouteCaptcha checkRouteCaptcha;
/**
* @description: 发送短信验证码
* @param: [phone]
@@ -32,26 +39,38 @@ public class SchisandraSmsController {
* @author zlg
* @date: 2024/5/8 22:53
*/
@PostMapping("/sendByTemplate/{phone}")
public Result sendByTemplate(@PathVariable("phone") String phone) {
String prefix = redisUtil.buildKey(AUTH_PHONE_PREFIX, phone);
String code = SmsUtils.getRandomInt(4);
if (!redisUtil.exist(prefix)) {
String key = redisUtil.buildKey(SMS_CONFIG_PREFIX, SmsConfigContext.SMS_CONFIG_KEY);
String configId = redisUtil.get(key);
if (configId == null) {
log.error("短信配置不存在!");
Result.fail("短信接口异常!");
}
SmsResponse smsResponse = SmsFactory.getSmsBlend(configId).sendMessage(phone, code);
if (smsResponse.isSuccess()) {
redisUtil.setNx(prefix, code, 60L * 5, SECONDS);
return Result.ok("短信发送成功5 分钟内有效,请注意查收!");
@PostMapping("/sendByTemplate")
public CaptchaResult sendByTemplate(@RequestBody SchisandraCaptchaDTO schisandraCaptchaDTO) {
if (schisandraCaptchaDTO.getToken() == null || schisandraCaptchaDTO.getDeg() == null || schisandraCaptchaDTO.getPhone() == null) {
return CaptchaResult.fail("验证失败!");
}
String token = schisandraCaptchaDTO.getToken();
String phone = schisandraCaptchaDTO.getPhone();
Double deg = schisandraCaptchaDTO.getDeg();
CaptchaResult captchaResult = checkRouteCaptcha.checkCaptcha(token, deg);
if (captchaResult.getCode() == 0) {
String prefix = redisUtil.buildKey(AUTH_PHONE_PREFIX, phone);
String code = SmsUtils.getRandomInt(4);
if (!redisUtil.exist(prefix)) {
String key = redisUtil.buildKey(SMS_CONFIG_PREFIX, SmsConfigContext.SMS_CONFIG_KEY);
String configId = redisUtil.get(key);
if (configId == null) {
log.error("短信配置不存在!");
CaptchaResult.fail("短信接口异常!");
}
SmsResponse smsResponse = SmsFactory.getSmsBlend(configId).sendMessage(phone, code);
if (smsResponse.isSuccess()) {
redisUtil.setNx(prefix, code, 60L * 5, SECONDS);
return CaptchaResult.ok("短信发送成功5 分钟内有效,请注意查收!");
} else {
return CaptchaResult.fail("短信发送失败,请重试!");
}
} else {
return Result.fail("短信发送失败,请重试!");
return CaptchaResult.ok("短信发送,请注意查看或稍后重试!");
}
} else {
return Result.fail("短信已发送,请注意查看或稍后重试");
return CaptchaResult.fail("验证失败");
}
}

View File

@@ -118,5 +118,15 @@ public class SchisandraAuthUserDTO implements Serializable {
*/
private String confirmPassword;
/**
* 旋转图片验证token
*/
private String token;
/**
* 旋转图片验证的旋转角度
*/
private Double deg;
}

View File

@@ -6,4 +6,5 @@ import lombok.Data;
public class SchisandraCaptchaDTO {
private String token;
private Double deg;
private String phone;
}

View File

@@ -0,0 +1,42 @@
package com.schisandra.auth.application.utils;
import com.schisandra.auth.common.entity.CaptchaResult;
import com.schisandra.auth.common.redis.RedisUtil;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @Classname CheckRouteCaptcha
* @BelongsProject: schisandra-cloud-storage
* @BelongsPackage: com.schisandra.auth.application.utils
* @Author: landaiqing
* @CreateTime: 2024-06-04 21:59
* @Description: TODO
* @Version: 1.0
*/
@Component
public class CheckRouteCaptcha {
@Resource
private RedisUtil redisUtil;
public final String authRotateCaptchaPrefix = "auth.rotate.captcha";
public CaptchaResult checkCaptcha(String token, Double deg) {
if (deg == null && token == null) {
return CaptchaResult.fail();
}
String prefix = redisUtil.buildKey(authRotateCaptchaPrefix, token);
if (redisUtil.exist(prefix)) {
double realNum = Double.parseDouble(redisUtil.get(prefix));
if (Math.abs(realNum - deg) / realNum < 0.05) {
redisUtil.del(prefix);
return CaptchaResult.ok();
} else {
return CaptchaResult.fail();
}
} else {
return CaptchaResult.fail();
}
}
}

View File

@@ -9,19 +9,22 @@ public class CaptchaResult<T> {
private String msg;
private Boolean success;
private T data;
public static CaptchaResult ok() {
CaptchaResult result = new CaptchaResult();
result.setCode(0);
result.setSuccess(true);
result.setMsg("Success");
return result;
}
public static <T> CaptchaResult ok(T data) {
CaptchaResult result = new CaptchaResult();
result.setSuccess(true);
result.setCode(0);
result.setMsg("Success");
result.setData(data);
@@ -30,14 +33,41 @@ public class CaptchaResult<T> {
public static CaptchaResult fail() {
CaptchaResult result = new CaptchaResult();
result.setSuccess(false);
result.setCode(1);
result.setMsg("验证失败");
return result;
}
public static CaptchaResult failWithSuccess() {
CaptchaResult result = new CaptchaResult();
result.setSuccess(true);
result.setCode(1);
result.setMsg("验证失败");
return result;
}
// 登录验证特殊情况
public static CaptchaResult failWithCode() {
CaptchaResult result = new CaptchaResult();
result.setSuccess(false);
result.setCode(0);
result.setMsg("验证成功");
return result;
}
public static <T> CaptchaResult failWithCode(T data) {
CaptchaResult result = new CaptchaResult();
result.setSuccess(false);
result.setCode(0);
result.setData(data);
result.setMsg("验证成功");
return result;
}
public static <T> CaptchaResult fail(T data) {
CaptchaResult result = new CaptchaResult();
result.setSuccess(false);
result.setCode(1);
result.setMsg("验证失败");
result.setData(data);