feat: 优化 captcha 代码

This commit is contained in:
xingyu 2023-01-12 21:51:53 +08:00
parent 865f101070
commit aaf62982e2
33 changed files with 471 additions and 2754 deletions

View File

@ -21,6 +21,7 @@
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId> <artifactId>spring-boot-starter</artifactId>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -1,13 +1,12 @@
package com.anji.captcha.config; package com.anji.captcha.config;
import cn.hutool.core.util.StrUtil;
import com.anji.captcha.model.common.Const; import com.anji.captcha.model.common.Const;
import com.anji.captcha.properties.AjCaptchaProperties; import com.anji.captcha.properties.AjCaptchaProperties;
import com.anji.captcha.service.CaptchaService; import com.anji.captcha.service.CaptchaService;
import com.anji.captcha.service.impl.CaptchaServiceFactory; import com.anji.captcha.service.impl.CaptchaServiceFactory;
import com.anji.captcha.util.ImageUtils; import com.anji.captcha.util.ImageUtils;
import com.anji.captcha.util.StringUtils; import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -21,15 +20,14 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
@Slf4j
@Configuration @Configuration
public class AjCaptchaServiceAutoConfiguration { public class AjCaptchaServiceAutoConfiguration {
private static Logger logger = LoggerFactory.getLogger(AjCaptchaServiceAutoConfiguration.class);
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public CaptchaService captchaService(AjCaptchaProperties prop) { public CaptchaService captchaService(AjCaptchaProperties prop) {
logger.info("自定义配置项:{}", prop.toString()); log.info("自定义配置项:{}", prop.toString());
Properties config = new Properties(); Properties config = new Properties();
config.put(Const.CAPTCHA_CACHETYPE, prop.getCacheType().name()); config.put(Const.CAPTCHA_CACHETYPE, prop.getCacheType().name());
config.put(Const.CAPTCHA_WATER_MARK, prop.getWaterMark()); config.put(Const.CAPTCHA_WATER_MARK, prop.getWaterMark());
@ -57,14 +55,13 @@ public class AjCaptchaServiceAutoConfiguration {
config.put(Const.CAPTCHA_FONT_STYLE, prop.getFontStyle() + ""); config.put(Const.CAPTCHA_FONT_STYLE, prop.getFontStyle() + "");
config.put(Const.CAPTCHA_WORD_COUNT, prop.getClickWordCount() + ""); config.put(Const.CAPTCHA_WORD_COUNT, prop.getClickWordCount() + "");
if ((StringUtils.isNotBlank(prop.getJigsaw()) && prop.getJigsaw().startsWith("classpath:")) if ((StrUtil.isNotBlank(prop.getJigsaw()) && prop.getJigsaw().startsWith("classpath:"))
|| (StringUtils.isNotBlank(prop.getPicClick()) && prop.getPicClick().startsWith("classpath:"))) { || (StrUtil.isNotBlank(prop.getPicClick()) && prop.getPicClick().startsWith("classpath:"))) {
//自定义resources目录下初始化底图 //自定义resources目录下初始化底图
config.put(Const.CAPTCHA_INIT_ORIGINAL, "true"); config.put(Const.CAPTCHA_INIT_ORIGINAL, "true");
initializeBaseMap(prop.getJigsaw(), prop.getPicClick()); initializeBaseMap(prop.getJigsaw(), prop.getPicClick());
} }
CaptchaService s = CaptchaServiceFactory.getInstance(config); return CaptchaServiceFactory.getInstance(config);
return s;
} }
private static void initializeBaseMap(String jigsaw, String picClick) { private static void initializeBaseMap(String jigsaw, String picClick) {

View File

@ -8,14 +8,13 @@ import org.springframework.context.annotation.Configuration;
/** /**
* 存储策略自动配置. * 存储策略自动配置.
*
*/ */
@Configuration @Configuration
public class AjCaptchaStorageAutoConfiguration { public class AjCaptchaStorageAutoConfiguration {
@Bean(name = "AjCaptchaCacheService") @Bean(name = "AjCaptchaCacheService")
public CaptchaCacheService captchaCacheService(AjCaptchaProperties ajCaptchaProperties){ public CaptchaCacheService captchaCacheService(AjCaptchaProperties ajCaptchaProperties) {
//缓存类型redis/local/.... // 缓存类型redis/local/....
return CaptchaServiceFactory.getCache(ajCaptchaProperties.getCacheType().name()); return CaptchaServiceFactory.getCache(ajCaptchaProperties.getCacheType().name());
} }
} }

View File

@ -1,65 +0,0 @@
/*
*Copyright © 2018 anji-plus
*安吉加加信息技术有限公司
*http://www.anji-plus.com
*All rights reserved.
*/
package com.anji.captcha.controller;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.anji.captcha.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
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.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/captcha")
public class CaptchaController {
@Autowired
private CaptchaService captchaService;
@PostMapping("/get")
public ResponseModel get(@RequestBody CaptchaVO data, HttpServletRequest request) {
assert request.getRemoteHost()!=null;
data.setBrowserInfo(getRemoteId(request));
return captchaService.get(data);
}
@PostMapping("/check")
public ResponseModel check(@RequestBody CaptchaVO data, HttpServletRequest request) {
data.setBrowserInfo(getRemoteId(request));
return captchaService.check(data);
}
//@PostMapping("/verify")
public ResponseModel verify(@RequestBody CaptchaVO data, HttpServletRequest request) {
return captchaService.verification(data);
}
public static final String getRemoteId(HttpServletRequest request) {
String xfwd = request.getHeader("X-Forwarded-For");
String ip = getRemoteIpFromXfwd(xfwd);
String ua = request.getHeader("user-agent");
if (StringUtils.isNotBlank(ip)) {
return ip + ua;
}
return request.getRemoteAddr() + ua;
}
private static String getRemoteIpFromXfwd(String xfwd) {
if (StringUtils.isNotBlank(xfwd)) {
String[] ipList = xfwd.split(",");
return StringUtils.trim(ipList[0]);
}
return null;
}
}

View File

@ -1,46 +1,46 @@
package com.anji.captcha.model.common; package com.anji.captcha.model.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
/** /**
* 底图类型枚举 * 底图类型枚举
*/ */
@Getter
@AllArgsConstructor
public enum CaptchaBaseMapEnum { public enum CaptchaBaseMapEnum {
ORIGINAL("ORIGINAL","滑动拼图底图"), ORIGINAL("ORIGINAL", "滑动拼图底图"),
SLIDING_BLOCK("SLIDING_BLOCK","滑动拼图滑块底图"), SLIDING_BLOCK("SLIDING_BLOCK", "滑动拼图滑块底图"),
PIC_CLICK("PIC_CLICK","文字点选底图"); PIC_CLICK("PIC_CLICK", "文字点选底图");
private String codeValue; private final String codeValue;
private String codeDesc; private final String codeDesc;
private CaptchaBaseMapEnum(String codeValue, String codeDesc) {
this.codeValue = codeValue;
this.codeDesc = codeDesc;
}
public String getCodeValue(){ return this.codeValue;}
public String getCodeDesc(){ return this.codeDesc;}
//根据codeValue获取枚举 //根据codeValue获取枚举
public static CaptchaBaseMapEnum parseFromCodeValue(String codeValue){ public static CaptchaBaseMapEnum parseFromCodeValue(String codeValue) {
for (CaptchaBaseMapEnum e : CaptchaBaseMapEnum.values()){ for (CaptchaBaseMapEnum e : CaptchaBaseMapEnum.values()) {
if(e.codeValue.equals(codeValue)){ return e;} if (e.codeValue.equals(codeValue)) {
return e;
}
} }
return null; return null;
} }
//根据codeValue获取描述 //根据codeValue获取描述
public static String getCodeDescByCodeBalue(String codeValue){ public static String getCodeDescByCodeBalue(String codeValue) {
CaptchaBaseMapEnum enumItem = parseFromCodeValue(codeValue); CaptchaBaseMapEnum enumItem = parseFromCodeValue(codeValue);
return enumItem == null ? "" : enumItem.getCodeDesc(); return enumItem == null ? "" : enumItem.getCodeDesc();
} }
//验证codeValue是否有效 //验证codeValue是否有效
public static boolean validateCodeValue(String codeValue){ return parseFromCodeValue(codeValue)!=null;} public static boolean validateCodeValue(String codeValue) {
return parseFromCodeValue(codeValue) != null;
}
//列出所有值字符串 //列出所有值字符串
public static String getString(){ public static String getString() {
StringBuffer buffer = new StringBuffer(); StringBuffer buffer = new StringBuffer();
for (CaptchaBaseMapEnum e : CaptchaBaseMapEnum.values()){ for (CaptchaBaseMapEnum e : CaptchaBaseMapEnum.values()) {
buffer.append(e.codeValue).append("--").append(e.getCodeDesc()).append(", "); buffer.append(e.codeValue).append("--").append(e.getCodeDesc()).append(", ");
} }
buffer.deleteCharAt(buffer.lastIndexOf(",")); buffer.deleteCharAt(buffer.lastIndexOf(","));

View File

@ -1,52 +1,52 @@
package com.anji.captcha.model.common; package com.anji.captcha.model.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum CaptchaTypeEnum { public enum CaptchaTypeEnum {
/** /**
* 滑块拼图. * 滑块拼图.
*/ */
BLOCKPUZZLE("blockPuzzle","滑块拼图"), BLOCKPUZZLE("blockPuzzle", "滑块拼图"),
/** /**
* 文字点选. * 文字点选.
*/ */
CLICKWORD("clickWord","文字点选"), CLICKWORD("clickWord", "文字点选"),
/** /**
* 默认. * 默认.
*/ */
DEFAULT("default","默认"); DEFAULT("default", "默认");
private String codeValue; private final String codeValue;
private String codeDesc; private final String codeDesc;
private CaptchaTypeEnum(String codeValue, String codeDesc) {
this.codeValue = codeValue;
this.codeDesc = codeDesc;
}
public String getCodeValue(){ return this.codeValue;}
public String getCodeDesc(){ return this.codeDesc;}
//根据codeValue获取枚举 //根据codeValue获取枚举
public static CaptchaTypeEnum parseFromCodeValue(String codeValue){ public static CaptchaTypeEnum parseFromCodeValue(String codeValue) {
for (CaptchaTypeEnum e : CaptchaTypeEnum.values()){ for (CaptchaTypeEnum e : CaptchaTypeEnum.values()) {
if(e.codeValue.equals(codeValue)){ return e;} if (e.codeValue.equals(codeValue)) {
return e;
}
} }
return null; return null;
} }
//根据codeValue获取描述 //根据codeValue获取描述
public static String getCodeDescByCodeBalue(String codeValue){ public static String getCodeDescByCodeBalue(String codeValue) {
CaptchaTypeEnum enumItem = parseFromCodeValue(codeValue); CaptchaTypeEnum enumItem = parseFromCodeValue(codeValue);
return enumItem == null ? "" : enumItem.getCodeDesc(); return enumItem == null ? "" : enumItem.getCodeDesc();
} }
//验证codeValue是否有效 //验证codeValue是否有效
public static boolean validateCodeValue(String codeValue){ return parseFromCodeValue(codeValue)!=null;} public static boolean validateCodeValue(String codeValue) {
return parseFromCodeValue(codeValue) != null;
}
//列出所有值字符串 //列出所有值字符串
public static String getString(){ public static String getString() {
StringBuffer buffer = new StringBuffer(); StringBuilder buffer = new StringBuilder();
for (CaptchaTypeEnum e : CaptchaTypeEnum.values()){ for (CaptchaTypeEnum e : CaptchaTypeEnum.values()) {
buffer.append(e.codeValue).append("--").append(e.getCodeDesc()).append(", "); buffer.append(e.codeValue).append("--").append(e.getCodeDesc()).append(", ");
} }
buffer.deleteCharAt(buffer.lastIndexOf(",")); buffer.deleteCharAt(buffer.lastIndexOf(","));

View File

@ -6,7 +6,7 @@ package com.anji.captcha.model.common;
public interface Const { public interface Const {
/** /**
*滑块底图路径 * 滑块底图路径
*/ */
String ORIGINAL_PATH_JIGSAW = "captcha.captchaOriginalPath.jigsaw"; String ORIGINAL_PATH_JIGSAW = "captcha.captchaOriginalPath.jigsaw";
@ -29,8 +29,8 @@ public interface Const {
* 点选文字验证码的文字字体(宋体) * 点选文字验证码的文字字体(宋体)
*/ */
String CAPTCHA_FONT_TYPE = "captcha.font.type"; String CAPTCHA_FONT_TYPE = "captcha.font.type";
String CAPTCHA_FONT_STYLE = "captcha.font.style"; String CAPTCHA_FONT_STYLE = "captcha.font.style";
String CAPTCHA_FONT_SIZE = "captcha.font.size"; String CAPTCHA_FONT_SIZE = "captcha.font.size";
/** /**
* 验证码类型default两种都实例化 * 验证码类型default两种都实例化
@ -72,41 +72,41 @@ public interface Const {
*/ */
String CAPTCHA_TIMING_CLEAR_SECOND = "captcha.timing.clear"; String CAPTCHA_TIMING_CLEAR_SECOND = "captcha.timing.clear";
/** /**
* 历史资源清除开关 0禁用,1 开启 * 历史资源清除开关 0禁用,1 开启
*/ */
String HISTORY_DATA_CLEAR_ENABLE = "captcha.history.data.clear.enable"; String HISTORY_DATA_CLEAR_ENABLE = "captcha.history.data.clear.enable";
/** /**
* 接口限流开关 0禁用 1启用 * 接口限流开关 0禁用 1启用
*/ */
String REQ_FREQUENCY_LIMIT_ENABLE = "captcha.req.frequency.limit.enable"; String REQ_FREQUENCY_LIMIT_ENABLE = "captcha.req.frequency.limit.enable";
/** /**
* get 接口 一分钟请求次数限制 * get 接口 一分钟请求次数限制
*/ */
String REQ_GET_MINUTE_LIMIT = "captcha.req.get.minute.limit"; String REQ_GET_MINUTE_LIMIT = "captcha.req.get.minute.limit";
/** /**
* 验证失败后get接口锁定时间 * 验证失败后get接口锁定时间
*/ */
String REQ_GET_LOCK_LIMIT = "captcha.req.get.lock.limit"; String REQ_GET_LOCK_LIMIT = "captcha.req.get.lock.limit";
/** /**
* 验证失败后get接口锁定时间 * 验证失败后get接口锁定时间
*/ */
String REQ_GET_LOCK_SECONDS = "captcha.req.get.lock.seconds"; String REQ_GET_LOCK_SECONDS = "captcha.req.get.lock.seconds";
/** /**
* verify 接口 一分钟请求次数限制 * verify 接口 一分钟请求次数限制
*/ */
String REQ_VALIDATE_MINUTE_LIMIT = "captcha.req.verify.minute.limit"; String REQ_VALIDATE_MINUTE_LIMIT = "captcha.req.verify.minute.limit";
/** /**
* check接口 一分钟请求次数限制 * check接口 一分钟请求次数限制
*/ */
String REQ_CHECK_MINUTE_LIMIT = "captcha.req.check.minute.limit"; String REQ_CHECK_MINUTE_LIMIT = "captcha.req.check.minute.limit";
/*** /***
* 点选文字个数 * 点选文字个数
*/ */
String CAPTCHA_WORD_COUNT = "captcha.word.count"; String CAPTCHA_WORD_COUNT = "captcha.word.count";
} }

View File

@ -6,16 +6,23 @@
*/ */
package com.anji.captcha.model.common; package com.anji.captcha.model.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.text.MessageFormat; import java.text.MessageFormat;
/** /**
* 返回应答码 * 返回应答码
* @author
* *
* @author
*/ */
@AllArgsConstructor
@Getter
public enum RepCodeEnum { public enum RepCodeEnum {
/** 0001 - 0099 网关应答码 */ /**
* 0001 - 0099 网关应答码
*/
SUCCESS("0000", "成功"), SUCCESS("0000", "成功"),
ERROR("0001", "操作失败"), ERROR("0001", "操作失败"),
EXCEPTION("9999", "服务器内部异常"), EXCEPTION("9999", "服务器内部异常"),
@ -33,36 +40,24 @@ public enum RepCodeEnum {
API_CAPTCHA_ERROR("6112", "获取验证码失败,请联系管理员"), API_CAPTCHA_ERROR("6112", "获取验证码失败,请联系管理员"),
API_CAPTCHA_BASEMAP_NULL("6113", "底图未初始化成功,请检查路径"), API_CAPTCHA_BASEMAP_NULL("6113", "底图未初始化成功,请检查路径"),
API_REQ_LIMIT_GET_ERROR("6201", "get接口请求次数超限请稍后再试!"), API_REQ_LIMIT_GET_ERROR("6201", "get接口请求次数超限请稍后再试!"),
API_REQ_INVALID("6206", "无效请求,请重新获取验证码"), API_REQ_INVALID("6206", "无效请求,请重新获取验证码"),
API_REQ_LOCK_GET_ERROR("6202", "接口验证失败数过多,请稍后再试"), API_REQ_LOCK_GET_ERROR("6202", "接口验证失败数过多,请稍后再试"),
API_REQ_LIMIT_CHECK_ERROR("6204", "check接口请求次数超限请稍后再试!"), API_REQ_LIMIT_CHECK_ERROR("6204", "check接口请求次数超限请稍后再试!"),
API_REQ_LIMIT_VERIFY_ERROR("6205", "verify请求次数超限!"), API_REQ_LIMIT_VERIFY_ERROR("6205", "verify请求次数超限!");
; private final String code;
private String code; private final String desc;
private String desc;
RepCodeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String getCode() {
return code;
}
public String getDesc() {
return desc;
}
public String getName(){
return this.name();
}
/** 将入参fieldNames与this.desc组合成错误信息 /**
* {fieldName}不能为空 * 将入参fieldNames与this.desc组合成错误信息
* {fieldName}不能为空
*
* @param fieldNames * @param fieldNames
* @return * @return
*/ */
public ResponseModel parseError(Object... fieldNames) { public ResponseModel parseError(Object... fieldNames) {
ResponseModel errorMessage=new ResponseModel(); ResponseModel errorMessage = new ResponseModel();
String newDesc = MessageFormat.format(this.desc, fieldNames); String newDesc = MessageFormat.format(this.desc, fieldNames);
errorMessage.setRepCode(this.code); errorMessage.setRepCode(this.code);

View File

@ -6,23 +6,32 @@
*/ */
package com.anji.captcha.model.common; package com.anji.captcha.model.common;
import com.anji.captcha.util.StringUtils;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@Data
public class RequestModel implements Serializable { public class RequestModel implements Serializable {
private static final long serialVersionUID = -5800786065305114784L; private static final long serialVersionUID = -5800786065305114784L;
/**当前请求接口路径 /business/accessUser/login */ /**
* 当前请求接口路径 /business/accessUser/login
*/
private String servletPath; private String servletPath;
/** {"reqData":{"password":"*****","userName":"admin"},"sign":"a304a7f296f565b6d2009797f68180f0","time":"1542456453355","token":""} */ /**
* {"reqData":{"password":"*****","userName":"admin"},"sign":"a304a7f296f565b6d2009797f68180f0","time":"1542456453355","token":""}
*/
private String requestString; private String requestString;
/** {"password":"****","userName":"admin"} */ /**
* {"password":"****","userName":"admin"}
*/
private HashMap reqData; private HashMap reqData;
private String token; private String token;
@ -46,10 +55,11 @@ public class RequestModel implements Serializable {
/** /**
* 校验自身参数合法性 * 校验自身参数合法性
*
* @return * @return
*/ */
public boolean isVaildateRequest() { public boolean isVaildateRequest() {
if (StringUtils.isBlank(sign) || StringUtils.isBlank(time)) { if (StrUtil.isBlank(sign) || StrUtil.isBlank(time)) {
return false; return false;
} }
return true; return true;
@ -63,95 +73,4 @@ public class RequestModel implements Serializable {
this.servletPath = servletPath; this.servletPath = servletPath;
} }
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public static long getSerialVersionUID() {
return serialVersionUID;
}
public List<Long> getProjectList() {
return projectList;
}
public void setProjectList(List<Long> projectList) {
this.projectList = projectList;
}
public List<Long> getGroupIdList() {
return groupIdList;
}
public void setGroupIdList(List<Long> groupIdList) {
this.groupIdList = groupIdList;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getSourceIP() {
return sourceIP;
}
public void setSourceIP(String sourceIP) {
this.sourceIP = sourceIP;
}
public String getRequestString() {
return requestString;
}
public void setRequestString(String requestString) {
this.requestString = requestString;
}
public HashMap getReqData() {
return reqData;
}
public void setReqData(HashMap reqData) {
this.reqData = reqData;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
} }

View File

@ -6,38 +6,42 @@
*/ */
package com.anji.captcha.model.common; package com.anji.captcha.model.common;
import com.anji.captcha.util.StringUtils; import cn.hutool.core.util.StrUtil;
import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
@Data
public class ResponseModel implements Serializable { public class ResponseModel implements Serializable {
private static final long serialVersionUID = 8445617032523881407L; private static final long serialVersionUID = 8445617032523881407L;
private String repCode; private String repCode;
private String repMsg; private String repMsg;
private Object repData; private Object repData;
public ResponseModel() { public ResponseModel() {
this.repCode = RepCodeEnum.SUCCESS.getCode(); this.repCode = RepCodeEnum.SUCCESS.getCode();
} }
public ResponseModel(RepCodeEnum repCodeEnum) { public ResponseModel(RepCodeEnum repCodeEnum) {
this.setRepCodeEnum(repCodeEnum); this.setRepCodeEnum(repCodeEnum);
} }
//成功 //成功
public static ResponseModel success(){ public static ResponseModel success() {
return ResponseModel.successMsg("成功"); return ResponseModel.successMsg("成功");
} }
public static ResponseModel successMsg(String message){
public static ResponseModel successMsg(String message) {
ResponseModel responseModel = new ResponseModel(); ResponseModel responseModel = new ResponseModel();
responseModel.setRepMsg(message); responseModel.setRepMsg(message);
return responseModel; return responseModel;
} }
public static ResponseModel successData(Object data){
public static ResponseModel successData(Object data) {
ResponseModel responseModel = new ResponseModel(); ResponseModel responseModel = new ResponseModel();
responseModel.setRepCode(RepCodeEnum.SUCCESS.getCode()); responseModel.setRepCode(RepCodeEnum.SUCCESS.getCode());
responseModel.setRepData(data); responseModel.setRepData(data);
@ -45,67 +49,45 @@ public class ResponseModel implements Serializable {
} }
//失败 //失败
public static ResponseModel errorMsg(RepCodeEnum message){ public static ResponseModel errorMsg(RepCodeEnum message) {
ResponseModel responseModel = new ResponseModel(); ResponseModel responseModel = new ResponseModel();
responseModel.setRepCodeEnum(message); responseModel.setRepCodeEnum(message);
return responseModel; return responseModel;
} }
public static ResponseModel errorMsg(String message){
public static ResponseModel errorMsg(String message) {
ResponseModel responseModel = new ResponseModel(); ResponseModel responseModel = new ResponseModel();
responseModel.setRepCode(RepCodeEnum.ERROR.getCode()); responseModel.setRepCode(RepCodeEnum.ERROR.getCode());
responseModel.setRepMsg(message); responseModel.setRepMsg(message);
return responseModel; return responseModel;
} }
public static ResponseModel errorMsg(RepCodeEnum repCodeEnum, String message){
public static ResponseModel errorMsg(RepCodeEnum repCodeEnum, String message) {
ResponseModel responseModel = new ResponseModel(); ResponseModel responseModel = new ResponseModel();
responseModel.setRepCode(repCodeEnum.getCode()); responseModel.setRepCode(repCodeEnum.getCode());
responseModel.setRepMsg(message); responseModel.setRepMsg(message);
return responseModel; return responseModel;
} }
public static ResponseModel exceptionMsg(String message){
public static ResponseModel exceptionMsg(String message) {
ResponseModel responseModel = new ResponseModel(); ResponseModel responseModel = new ResponseModel();
responseModel.setRepCode(RepCodeEnum.EXCEPTION.getCode()); responseModel.setRepCode(RepCodeEnum.EXCEPTION.getCode());
responseModel.setRepMsg(RepCodeEnum.EXCEPTION.getDesc() + ": " + message); responseModel.setRepMsg(RepCodeEnum.EXCEPTION.getDesc() + ": " + message);
return responseModel; return responseModel;
} }
@Override
public String toString() {
return "ResponseModel{" + "repCode='" + repCode + '\'' + ", repMsg='"
+ repMsg + '\'' + ", repData=" + repData + '}';
}
public boolean isSuccess(){ public boolean isSuccess() {
return StringUtils.equals(repCode, RepCodeEnum.SUCCESS.getCode()); return StrUtil.equals(repCode, RepCodeEnum.SUCCESS.getCode());
} }
public String getRepCode() { public String getRepCode() {
return repCode; return repCode;
} }
public void setRepCode(String repCode) {
this.repCode = repCode;
}
public void setRepCodeEnum(RepCodeEnum repCodeEnum) { public void setRepCodeEnum(RepCodeEnum repCodeEnum) {
this.repCode=repCodeEnum.getCode(); this.repCode = repCodeEnum.getCode();
this.repMsg=repCodeEnum.getDesc(); this.repMsg = repCodeEnum.getDesc();
} }
public String getRepMsg() {
return repMsg;
}
public void setRepMsg(String repMsg) {
this.repMsg = repMsg;
}
public Object getRepData() {
return repData;
}
public void setRepData(Object repData) {
this.repData = repData;
}
} }

View File

@ -6,10 +6,13 @@
*/ */
package com.anji.captcha.model.vo; package com.anji.captcha.model.vo;
import lombok.Data;
import java.awt.*; import java.awt.*;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
@Data
public class CaptchaVO implements Serializable { public class CaptchaVO implements Serializable {
/** /**
@ -79,173 +82,23 @@ public class CaptchaVO implements Serializable {
*/ */
private String captchaVerification; private String captchaVerification;
/*** /***
* 客户端UI组件id,组件初始化时设置一次UUID * 客户端UI组件id,组件初始化时设置一次UUID
*/ */
private String clientUid; private String clientUid;
/*** /***
* 客户端的请求时间预留字段 * 客户端的请求时间预留字段
*/ */
private Long ts; private Long ts;
/*** /***
* 客户端ip+userAgent * 客户端ip+userAgent
*/ */
private String browserInfo; private String browserInfo;
public void resetClientFlag(){
public void resetClientFlag() {
this.browserInfo = null; this.browserInfo = null;
this.clientUid = null; this.clientUid = null;
} }
public String getCaptchaId() {
return captchaId;
}
public void setCaptchaId(String captchaId) {
this.captchaId = captchaId;
}
public String getProjectCode() {
return projectCode;
}
public void setProjectCode(String projectCode) {
this.projectCode = projectCode;
}
public String getCaptchaType() {
return captchaType;
}
public void setCaptchaType(String captchaType) {
this.captchaType = captchaType;
}
public String getCaptchaOriginalPath() {
return captchaOriginalPath;
}
public void setCaptchaOriginalPath(String captchaOriginalPath) {
this.captchaOriginalPath = captchaOriginalPath;
}
public String getCaptchaFontType() {
return captchaFontType;
}
public void setCaptchaFontType(String captchaFontType) {
this.captchaFontType = captchaFontType;
}
public Integer getCaptchaFontSize() {
return captchaFontSize;
}
public void setCaptchaFontSize(Integer captchaFontSize) {
this.captchaFontSize = captchaFontSize;
}
public String getOriginalImageBase64() {
return originalImageBase64;
}
public void setOriginalImageBase64(String originalImageBase64) {
this.originalImageBase64 = originalImageBase64;
}
public PointVO getPoint() {
return point;
}
public void setPoint(PointVO point) {
this.point = point;
}
public String getJigsawImageBase64() {
return jigsawImageBase64;
}
public void setJigsawImageBase64(String jigsawImageBase64) {
this.jigsawImageBase64 = jigsawImageBase64;
}
public List<String> getWordList() {
return wordList;
}
public void setWordList(List<String> wordList) {
this.wordList = wordList;
}
public List<Point> getPointList() {
return pointList;
}
public void setPointList(List<Point> pointList) {
this.pointList = pointList;
}
public String getPointJson() {
return pointJson;
}
public void setPointJson(String pointJson) {
this.pointJson = pointJson;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Boolean getResult() {
return result;
}
public void setResult(Boolean result) {
this.result = result;
}
public String getCaptchaVerification() {
return captchaVerification;
}
public void setCaptchaVerification(String captchaVerification) {
this.captchaVerification = captchaVerification;
}
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
public String getClientUid() {
return clientUid;
}
public void setClientUid(String clientUid) {
this.clientUid = clientUid;
}
public Long getTs() {
return ts;
}
public void setTs(Long ts) {
this.ts = ts;
}
public String getBrowserInfo() {
return browserInfo;
}
public void setBrowserInfo(String browserInfo) {
this.browserInfo = browserInfo;
}
} }

View File

@ -1,5 +1,7 @@
package com.anji.captcha.model.vo; package com.anji.captcha.model.vo;
import lombok.Data;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -9,6 +11,7 @@ import java.util.regex.Pattern;
/** /**
* Created by raodeming on 2020/5/16. * Created by raodeming on 2020/5/16.
*/ */
@Data
public class PointVO { public class PointVO {
private String secretKey; private String secretKey;
@ -16,30 +19,6 @@ public class PointVO {
public int y; public int y;
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public PointVO(int x, int y, String secretKey) { public PointVO(int x, int y, String secretKey) {
this.secretKey = secretKey; this.secretKey = secretKey;
this.x = x; this.x = x;

View File

@ -1,6 +1,7 @@
package com.anji.captcha.properties; package com.anji.captcha.properties;
import com.anji.captcha.model.common.CaptchaTypeEnum; import com.anji.captcha.model.common.CaptchaTypeEnum;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import java.awt.*; import java.awt.*;
@ -8,6 +9,7 @@ import java.awt.*;
import static com.anji.captcha.properties.AjCaptchaProperties.PREFIX; import static com.anji.captcha.properties.AjCaptchaProperties.PREFIX;
import static com.anji.captcha.properties.AjCaptchaProperties.StorageType.local; import static com.anji.captcha.properties.AjCaptchaProperties.StorageType.local;
@Data
@ConfigurationProperties(PREFIX) @ConfigurationProperties(PREFIX)
public class AjCaptchaProperties { public class AjCaptchaProperties {
public static final String PREFIX = "aj.captcha"; public static final String PREFIX = "aj.captcha";
@ -113,90 +115,10 @@ public class AjCaptchaProperties {
*/ */
private int clickWordCount = 4; private int clickWordCount = 4;
public int getFontStyle() {
return fontStyle;
}
public void setFontStyle(int fontStyle) {
this.fontStyle = fontStyle;
}
public int getFontSize() {
return fontSize;
}
public void setFontSize(int fontSize) {
this.fontSize = fontSize;
}
public int getClickWordCount() {
return clickWordCount;
}
public void setClickWordCount(int clickWordCount) {
this.clickWordCount = clickWordCount;
}
public boolean isHistoryDataClearEnable() {
return historyDataClearEnable;
}
public void setHistoryDataClearEnable(boolean historyDataClearEnable) {
this.historyDataClearEnable = historyDataClearEnable;
}
public boolean isReqFrequencyLimitEnable() {
return reqFrequencyLimitEnable;
}
public boolean getReqFrequencyLimitEnable() { public boolean getReqFrequencyLimitEnable() {
return reqFrequencyLimitEnable; return reqFrequencyLimitEnable;
} }
public void setReqFrequencyLimitEnable(boolean reqFrequencyLimitEnable) {
this.reqFrequencyLimitEnable = reqFrequencyLimitEnable;
}
public int getReqGetLockLimit() {
return reqGetLockLimit;
}
public void setReqGetLockLimit(int reqGetLockLimit) {
this.reqGetLockLimit = reqGetLockLimit;
}
public int getReqGetLockSeconds() {
return reqGetLockSeconds;
}
public void setReqGetLockSeconds(int reqGetLockSeconds) {
this.reqGetLockSeconds = reqGetLockSeconds;
}
public int getReqGetMinuteLimit() {
return reqGetMinuteLimit;
}
public void setReqGetMinuteLimit(int reqGetMinuteLimit) {
this.reqGetMinuteLimit = reqGetMinuteLimit;
}
public int getReqCheckMinuteLimit() {
return reqGetMinuteLimit;
}
public void setReqCheckMinuteLimit(int reqCheckMinuteLimit) {
this.reqCheckMinuteLimit = reqCheckMinuteLimit;
}
public int getReqVerifyMinuteLimit() {
return reqVerifyMinuteLimit;
}
public void setReqVerifyMinuteLimit(int reqVerifyMinuteLimit) {
this.reqVerifyMinuteLimit = reqVerifyMinuteLimit;
}
public enum StorageType { public enum StorageType {
/** /**
* 内存. * 内存.
@ -216,123 +138,4 @@ public class AjCaptchaProperties {
return PREFIX; return PREFIX;
} }
public CaptchaTypeEnum getType() {
return type;
}
public void setType(CaptchaTypeEnum type) {
this.type = type;
}
public String getJigsaw() {
return jigsaw;
}
public void setJigsaw(String jigsaw) {
this.jigsaw = jigsaw;
}
public String getPicClick() {
return picClick;
}
public void setPicClick(String picClick) {
this.picClick = picClick;
}
public String getWaterMark() {
return waterMark;
}
public void setWaterMark(String waterMark) {
this.waterMark = waterMark;
}
public String getWaterFont() {
return waterFont;
}
public void setWaterFont(String waterFont) {
this.waterFont = waterFont;
}
public String getFontType() {
return fontType;
}
public void setFontType(String fontType) {
this.fontType = fontType;
}
public String getSlipOffset() {
return slipOffset;
}
public void setSlipOffset(String slipOffset) {
this.slipOffset = slipOffset;
}
public Boolean getAesStatus() {
return aesStatus;
}
public void setAesStatus(Boolean aesStatus) {
this.aesStatus = aesStatus;
}
public StorageType getCacheType() {
return cacheType;
}
public void setCacheType(StorageType cacheType) {
this.cacheType = cacheType;
}
public String getInterferenceOptions() {
return interferenceOptions;
}
public void setInterferenceOptions(String interferenceOptions) {
this.interferenceOptions = interferenceOptions;
}
public String getCacheNumber() {
return cacheNumber;
}
public void setCacheNumber(String cacheNumber) {
this.cacheNumber = cacheNumber;
}
public String getTimingClear() {
return timingClear;
}
public void setTimingClear(String timingClear) {
this.timingClear = timingClear;
}
@Override
public String toString() {
return "\nAjCaptchaProperties{" +
"type=" + type +
", jigsaw='" + jigsaw + '\'' +
", picClick='" + picClick + '\'' +
", waterMark='" + waterMark + '\'' +
", waterFont='" + waterFont + '\'' +
", fontType='" + fontType + '\'' +
", slipOffset='" + slipOffset + '\'' +
", aesStatus=" + aesStatus +
", interferenceOptions='" + interferenceOptions + '\'' +
", cacheNumber='" + cacheNumber + '\'' +
", timingClear='" + timingClear + '\'' +
", cacheType=" + cacheType +
", reqFrequencyLimitEnable=" + reqFrequencyLimitEnable +
", reqGetLockLimit=" + reqGetLockLimit +
", reqGetLockSeconds=" + reqGetLockSeconds +
", reqGetMinuteLimit=" + reqGetMinuteLimit +
", reqCheckMinuteLimit=" + reqCheckMinuteLimit +
", reqVerifyMinuteLimit=" + reqVerifyMinuteLimit +
'}';
}
} }

View File

@ -8,34 +8,36 @@ package com.anji.captcha.service;
/** /**
* 验证码缓存接口 * 验证码缓存接口
*
* @author lide1202@hotmail.com * @author lide1202@hotmail.com
* @date 2018-08-21 * @date 2018-08-21
*/ */
public interface CaptchaCacheService { public interface CaptchaCacheService {
void set(String key, String value, long expiresInSeconds); void set(String key, String value, long expiresInSeconds);
boolean exists(String key); boolean exists(String key);
void delete(String key); void delete(String key);
String get(String key); String get(String key);
/** /**
* 缓存类型-local/redis/memcache/.. * 缓存类型-local/redis/memcache/..
* 通过java SPI机制接入方可自定义实现类 * 通过java SPI机制接入方可自定义实现类
* @return *
*/ * @return
String type(); */
String type();
/*** /***
* *
* @param key * @param key
* @param val * @param val
* @return * @return
*/ */
default Long increment(String key, long val){ default Long increment(String key, long val) {
return 0L; return 0L;
}; }
} }

View File

@ -13,6 +13,7 @@ import java.util.Properties;
/** /**
* 验证码服务接口 * 验证码服务接口
*
* @author lide1202@hotmail.com * @author lide1202@hotmail.com
* @date 2020-05-12 * @date 2020-05-12
*/ */
@ -24,6 +25,7 @@ public interface CaptchaService {
/** /**
* 获取验证码 * 获取验证码
*
* @param captchaVO * @param captchaVO
* @return * @return
*/ */
@ -31,6 +33,7 @@ public interface CaptchaService {
/** /**
* 核对验证码(前端) * 核对验证码(前端)
*
* @param captchaVO * @param captchaVO
* @return * @return
*/ */
@ -38,6 +41,7 @@ public interface CaptchaService {
/** /**
* 二次校验验证码(后端) * 二次校验验证码(后端)
*
* @param captchaVO * @param captchaVO
* @return * @return
*/ */
@ -50,9 +54,10 @@ public interface CaptchaService {
*/ */
String captchaType(); String captchaType();
/** /**
* 历史资源清除(过期的图片文件生成的临时图片...) * 历史资源清除(过期的图片文件生成的临时图片...)
* @param config 配置项 控制资源清理的粒度 *
*/ * @param config 配置项 控制资源清理的粒度
void destroy(Properties config); */
void destroy(Properties config);
} }

View File

@ -6,6 +6,7 @@
*/ */
package com.anji.captcha.service.impl; package com.anji.captcha.service.impl;
import cn.hutool.core.util.StrUtil;
import com.anji.captcha.model.common.Const; import com.anji.captcha.model.common.Const;
import com.anji.captcha.model.common.RepCodeEnum; import com.anji.captcha.model.common.RepCodeEnum;
import com.anji.captcha.model.common.ResponseModel; import com.anji.captcha.model.common.ResponseModel;
@ -13,29 +14,28 @@ import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaCacheService; import com.anji.captcha.service.CaptchaCacheService;
import com.anji.captcha.service.CaptchaService; import com.anji.captcha.service.CaptchaService;
import com.anji.captcha.util.*; import com.anji.captcha.util.*;
import org.slf4j.Logger; import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import java.awt.*; import java.awt.*;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Base64; import java.util.Base64;
import java.util.Objects;
import java.util.Properties; import java.util.Properties;
/** /**
* Created by raodeming on 2019/12/25. * Created by raodeming on 2019/12/25.
*/ */
@Slf4j
public abstract class AbstractCaptchaService implements CaptchaService { public abstract class AbstractCaptchaService implements CaptchaService {
protected Logger logger = LoggerFactory.getLogger(getClass());
protected static final String IMAGE_TYPE_PNG = "png"; protected static final String IMAGE_TYPE_PNG = "png";
protected static int HAN_ZI_SIZE = 25; protected static int HAN_ZI_SIZE = 25;
protected static int HAN_ZI_SIZE_HALF = HAN_ZI_SIZE / 2; protected static int HAN_ZI_SIZE_HALF = HAN_ZI_SIZE / 2;
//check校验坐标 //check校验坐标
protected static String REDIS_CAPTCHA_KEY = "RUNNING:CAPTCHA:%s"; protected static String REDIS_CAPTCHA_KEY = "RUNNING:CAPTCHA:%s";
@ -73,7 +73,7 @@ public abstract class AbstractCaptchaService implements CaptchaService {
ImageUtils.cacheImage(config.getProperty(Const.ORIGINAL_PATH_JIGSAW), ImageUtils.cacheImage(config.getProperty(Const.ORIGINAL_PATH_JIGSAW),
config.getProperty(Const.ORIGINAL_PATH_PIC_CLICK)); config.getProperty(Const.ORIGINAL_PATH_PIC_CLICK));
} }
logger.info("--->>>初始化验证码底图<<<---" + captchaType()); log.info("--->>>初始化验证码底图<<<---" + captchaType());
waterMark = config.getProperty(Const.CAPTCHA_WATER_MARK, "我的水印"); waterMark = config.getProperty(Const.CAPTCHA_WATER_MARK, "我的水印");
slipOffset = config.getProperty(Const.CAPTCHA_SLIP_OFFSET, "5"); slipOffset = config.getProperty(Const.CAPTCHA_SLIP_OFFSET, "5");
waterMarkFontStr = config.getProperty(Const.CAPTCHA_WATER_FONT, "WenQuanZhengHei.ttf"); waterMarkFontStr = config.getProperty(Const.CAPTCHA_WATER_FONT, "WenQuanZhengHei.ttf");
@ -88,13 +88,13 @@ public abstract class AbstractCaptchaService implements CaptchaService {
// 通过加载resources下的font字体解决无需在linux中安装字体 // 通过加载resources下的font字体解决无需在linux中安装字体
loadWaterMarkFont(); loadWaterMarkFont();
if (cacheType.equals("local")) { if ("local".equals(cacheType)) {
logger.info("初始化local缓存..."); log.info("初始化local缓存...");
CacheUtil.init(Integer.parseInt(config.getProperty(Const.CAPTCHA_CACAHE_MAX_NUMBER, "1000")), CacheUtil.init(Integer.parseInt(config.getProperty(Const.CAPTCHA_CACAHE_MAX_NUMBER, "1000")),
Long.parseLong(config.getProperty(Const.CAPTCHA_TIMING_CLEAR_SECOND, "180"))); Long.parseLong(config.getProperty(Const.CAPTCHA_TIMING_CLEAR_SECOND, "180")));
} }
if (config.getProperty(Const.HISTORY_DATA_CLEAR_ENABLE, "0").equals("1")) { if ("1".equals(config.getProperty(Const.HISTORY_DATA_CLEAR_ENABLE, "0"))) {
logger.info("历史资源清除开关...开启..." + captchaType()); log.info("历史资源清除开关...开启..." + captchaType());
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -102,9 +102,9 @@ public abstract class AbstractCaptchaService implements CaptchaService {
} }
})); }));
} }
if (config.getProperty(Const.REQ_FREQUENCY_LIMIT_ENABLE, "0").equals("1")) { if ("1".equals(config.getProperty(Const.REQ_FREQUENCY_LIMIT_ENABLE, "0"))) {
if (limitHandler == null) { if (limitHandler == null) {
logger.info("接口分钟内限流开关...开启..."); log.info("接口分钟内限流开关...开启...");
limitHandler = new FrequencyLimitHandler.DefaultLimitHandler(config, getCacheService(cacheType)); limitHandler = new FrequencyLimitHandler.DefaultLimitHandler(config, getCacheService(cacheType));
} }
} }
@ -150,7 +150,7 @@ public abstract class AbstractCaptchaService implements CaptchaService {
if (captchaVO == null) { if (captchaVO == null) {
return RepCodeEnum.NULL_ERROR.parseError("captchaVO"); return RepCodeEnum.NULL_ERROR.parseError("captchaVO");
} }
if (StringUtils.isEmpty(captchaVO.getCaptchaVerification())) { if (StrUtil.isEmpty(captchaVO.getCaptchaVerification())) {
return RepCodeEnum.NULL_ERROR.parseError("captchaVerification"); return RepCodeEnum.NULL_ERROR.parseError("captchaVerification");
} }
if (limitHandler != null) { if (limitHandler != null) {
@ -163,17 +163,17 @@ public abstract class AbstractCaptchaService implements CaptchaService {
return resp == null || resp.isSuccess(); return resp == null || resp.isSuccess();
} }
protected String getValidateClientId(CaptchaVO req){ protected String getValidateClientId(CaptchaVO req) {
// 以服务端获取的客户端标识 做识别标志 // 以服务端获取的客户端标识 做识别标志
if(StringUtils.isNotEmpty(req.getBrowserInfo())){ if (StrUtil.isNotEmpty(req.getBrowserInfo())) {
return MD5Util.md5(req.getBrowserInfo()); return MD5Util.md5(req.getBrowserInfo());
} }
// 以客户端Ui组件id做识别标志 // 以客户端Ui组件id做识别标志
if(StringUtils.isNotEmpty(req.getClientUid())){ if (StrUtil.isNotEmpty(req.getClientUid())) {
return req.getClientUid(); return req.getClientUid();
} }
return null; return null;
} }
protected void afterValidateFail(CaptchaVO data) { protected void afterValidateFail(CaptchaVO data) {
if (limitHandler != null) { if (limitHandler != null) {
@ -197,14 +197,14 @@ public abstract class AbstractCaptchaService implements CaptchaService {
if (waterMarkFontStr.toLowerCase().endsWith(".ttf") || waterMarkFontStr.toLowerCase().endsWith(".ttc") if (waterMarkFontStr.toLowerCase().endsWith(".ttf") || waterMarkFontStr.toLowerCase().endsWith(".ttc")
|| waterMarkFontStr.toLowerCase().endsWith(".otf")) { || waterMarkFontStr.toLowerCase().endsWith(".otf")) {
this.waterMarkFont = Font.createFont(Font.TRUETYPE_FONT, this.waterMarkFont = Font.createFont(Font.TRUETYPE_FONT,
getClass().getResourceAsStream("/fonts/" + waterMarkFontStr)) Objects.requireNonNull(getClass().getResourceAsStream("/fonts/" + waterMarkFontStr)))
.deriveFont(Font.BOLD, HAN_ZI_SIZE / 2); .deriveFont(Font.BOLD, HAN_ZI_SIZE / 2);
} else { } else {
this.waterMarkFont = new Font(waterMarkFontStr, Font.BOLD, HAN_ZI_SIZE / 2); this.waterMarkFont = new Font(waterMarkFontStr, Font.BOLD, HAN_ZI_SIZE / 2);
} }
} catch (Exception e) { } catch (Exception e) {
logger.error("load font error:{}", e); log.error("load font error:{}", e);
} }
} }
@ -228,7 +228,7 @@ public abstract class AbstractCaptchaService implements CaptchaService {
if (!tempFile.getParentFile().exists()) { if (!tempFile.getParentFile().exists()) {
tempFile.getParentFile().mkdirs(); tempFile.getParentFile().mkdirs();
} }
OutputStream out = new FileOutputStream(tempFile); OutputStream out = Files.newOutputStream(tempFile.toPath());
out.write(b); out.write(b);
out.flush(); out.flush();
out.close(); out.close();

View File

@ -6,12 +6,14 @@
*/ */
package com.anji.captcha.service.impl; package com.anji.captcha.service.impl;
import cn.hutool.core.util.StrUtil;
import com.anji.captcha.model.common.CaptchaTypeEnum; import com.anji.captcha.model.common.CaptchaTypeEnum;
import com.anji.captcha.model.common.RepCodeEnum; import com.anji.captcha.model.common.RepCodeEnum;
import com.anji.captcha.model.common.ResponseModel; import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO; import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.model.vo.PointVO; import com.anji.captcha.model.vo.PointVO;
import com.anji.captcha.util.*; import com.anji.captcha.util.*;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.*; import java.awt.*;
@ -27,6 +29,7 @@ import java.util.Random;
* <p> * <p>
* Created by raodeming on 2019/12/25. * Created by raodeming on 2019/12/25.
*/ */
@Slf4j
public class BlockPuzzleCaptchaServiceImpl extends AbstractCaptchaService { public class BlockPuzzleCaptchaServiceImpl extends AbstractCaptchaService {
@Override @Override
@ -34,10 +37,10 @@ public class BlockPuzzleCaptchaServiceImpl extends AbstractCaptchaService {
super.init(config); super.init(config);
} }
@Override @Override
public void destroy(Properties config) { public void destroy(Properties config) {
logger.info("start-clear-history-data-",captchaType()); log.info("start-clear-history-data-", captchaType());
} }
@Override @Override
public String captchaType() { public String captchaType() {
@ -46,14 +49,14 @@ public class BlockPuzzleCaptchaServiceImpl extends AbstractCaptchaService {
@Override @Override
public ResponseModel get(CaptchaVO captchaVO) { public ResponseModel get(CaptchaVO captchaVO) {
ResponseModel r = super.get(captchaVO); ResponseModel r = super.get(captchaVO);
if(!validatedReq(r)){ if (!validatedReq(r)) {
return r; return r;
} }
//原生图片 //原生图片
BufferedImage originalImage = ImageUtils.getOriginal(); BufferedImage originalImage = ImageUtils.getOriginal();
if (null == originalImage) { if (null == originalImage) {
logger.error("滑动底图未初始化成功,请检查路径"); log.error("滑动底图未初始化成功,请检查路径");
return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_BASEMAP_NULL); return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_BASEMAP_NULL);
} }
//设置水印 //设置水印
@ -68,13 +71,13 @@ public class BlockPuzzleCaptchaServiceImpl extends AbstractCaptchaService {
String jigsawImageBase64 = ImageUtils.getslidingBlock(); String jigsawImageBase64 = ImageUtils.getslidingBlock();
BufferedImage jigsawImage = ImageUtils.getBase64StrToImage(jigsawImageBase64); BufferedImage jigsawImage = ImageUtils.getBase64StrToImage(jigsawImageBase64);
if (null == jigsawImage) { if (null == jigsawImage) {
logger.error("滑动底图未初始化成功,请检查路径"); log.error("滑动底图未初始化成功,请检查路径");
return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_BASEMAP_NULL); return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_BASEMAP_NULL);
} }
CaptchaVO captcha = pictureTemplatesCut(originalImage, jigsawImage, jigsawImageBase64); CaptchaVO captcha = pictureTemplatesCut(originalImage, jigsawImage, jigsawImageBase64);
if (captcha == null if (captcha == null
|| StringUtils.isBlank(captcha.getJigsawImageBase64()) || StrUtil.isBlank(captcha.getJigsawImageBase64())
|| StringUtils.isBlank(captcha.getOriginalImageBase64())) { || StrUtil.isBlank(captcha.getOriginalImageBase64())) {
return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_ERROR); return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_ERROR);
} }
return ResponseModel.successData(captcha); return ResponseModel.successData(captcha);
@ -82,10 +85,10 @@ public class BlockPuzzleCaptchaServiceImpl extends AbstractCaptchaService {
@Override @Override
public ResponseModel check(CaptchaVO captchaVO) { public ResponseModel check(CaptchaVO captchaVO) {
ResponseModel r = super.check(captchaVO); ResponseModel r = super.check(captchaVO);
if(!validatedReq(r)){ if (!validatedReq(r)) {
return r; return r;
} }
//取坐标信息 //取坐标信息
String codeKey = String.format(REDIS_CAPTCHA_KEY, captchaVO.getToken()); String codeKey = String.format(REDIS_CAPTCHA_KEY, captchaVO.getToken());
if (!CaptchaServiceFactory.getCache(cacheType).exists(codeKey)) { if (!CaptchaServiceFactory.getCache(cacheType).exists(codeKey)) {
@ -103,7 +106,7 @@ public class BlockPuzzleCaptchaServiceImpl extends AbstractCaptchaService {
pointJson = decrypt(captchaVO.getPointJson(), point.getSecretKey()); pointJson = decrypt(captchaVO.getPointJson(), point.getSecretKey());
point1 = JsonUtil.parseObject(pointJson, PointVO.class); point1 = JsonUtil.parseObject(pointJson, PointVO.class);
} catch (Exception e) { } catch (Exception e) {
logger.error("验证码坐标解析失败", e); log.error("验证码坐标解析失败", e);
afterValidateFail(captchaVO); afterValidateFail(captchaVO);
return ResponseModel.errorMsg(e.getMessage()); return ResponseModel.errorMsg(e.getMessage());
} }
@ -119,7 +122,7 @@ public class BlockPuzzleCaptchaServiceImpl extends AbstractCaptchaService {
try { try {
value = AESUtil.aesEncrypt(captchaVO.getToken().concat("---").concat(pointJson), secretKey); value = AESUtil.aesEncrypt(captchaVO.getToken().concat("---").concat(pointJson), secretKey);
} catch (Exception e) { } catch (Exception e) {
logger.error("AES加密失败", e); log.error("AES加密失败", e);
afterValidateFail(captchaVO); afterValidateFail(captchaVO);
return ResponseModel.errorMsg(e.getMessage()); return ResponseModel.errorMsg(e.getMessage());
} }
@ -132,10 +135,10 @@ public class BlockPuzzleCaptchaServiceImpl extends AbstractCaptchaService {
@Override @Override
public ResponseModel verification(CaptchaVO captchaVO) { public ResponseModel verification(CaptchaVO captchaVO) {
ResponseModel r = super.verification(captchaVO); ResponseModel r = super.verification(captchaVO);
if(!validatedReq(r)){ if (!validatedReq(r)) {
return r; return r;
} }
try { try {
String codeKey = String.format(REDIS_SECOND_CAPTCHA_KEY, captchaVO.getCaptchaVerification()); String codeKey = String.format(REDIS_SECOND_CAPTCHA_KEY, captchaVO.getCaptchaVerification());
if (!CaptchaServiceFactory.getCache(cacheType).exists(codeKey)) { if (!CaptchaServiceFactory.getCache(cacheType).exists(codeKey)) {
@ -144,7 +147,7 @@ public class BlockPuzzleCaptchaServiceImpl extends AbstractCaptchaService {
//二次校验取值后即刻失效 //二次校验取值后即刻失效
CaptchaServiceFactory.getCache(cacheType).delete(codeKey); CaptchaServiceFactory.getCache(cacheType).delete(codeKey);
} catch (Exception e) { } catch (Exception e) {
logger.error("验证码坐标解析失败", e); log.error("验证码坐标解析失败", e);
return ResponseModel.errorMsg(e.getMessage()); return ResponseModel.errorMsg(e.getMessage());
} }
return ResponseModel.success(); return ResponseModel.success();
@ -234,7 +237,7 @@ public class BlockPuzzleCaptchaServiceImpl extends AbstractCaptchaService {
//将坐标信息存入redis中 //将坐标信息存入redis中
String codeKey = String.format(REDIS_CAPTCHA_KEY, dataVO.getToken()); String codeKey = String.format(REDIS_CAPTCHA_KEY, dataVO.getToken());
CaptchaServiceFactory.getCache(cacheType).set(codeKey, JsonUtil.toJSONString(point), EXPIRESIN_SECONDS); CaptchaServiceFactory.getCache(cacheType).set(codeKey, JsonUtil.toJSONString(point), EXPIRESIN_SECONDS);
logger.debug("token{},point:{}", dataVO.getToken(), JsonUtil.toJSONString(point)); log.debug("token{},point:{}", dataVO.getToken(), JsonUtil.toJSONString(point));
return dataVO; return dataVO;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();

View File

@ -3,12 +3,15 @@ package com.anji.captcha.service.impl;
import com.anji.captcha.service.CaptchaCacheService; import com.anji.captcha.service.CaptchaCacheService;
import com.anji.captcha.util.CacheUtil; import com.anji.captcha.util.CacheUtil;
import java.util.Objects;
/** /**
* 对于分布式部署的应用我们建议应用自己实现CaptchaCacheService比如用Redis参考service/spring-boot代码示例 * 对于分布式部署的应用我们建议应用自己实现CaptchaCacheService比如用Redis参考service/spring-boot代码示例
* 如果应用是单点的也没有使用redis那默认使用内存 * 如果应用是单点的也没有使用redis那默认使用内存
* 内存缓存只适合单节点部署的应用否则验证码生产与验证在节点之间信息不同步导致失败 * 内存缓存只适合单节点部署的应用否则验证码生产与验证在节点之间信息不同步导致失败
* @Title: 默认使用内存当缓存 *
* @author lide1202@hotmail.com * @author lide1202@hotmail.com
* @Title: 默认使用内存当缓存
* @date 2020-05-12 * @date 2020-05-12
*/ */
public class CaptchaCacheServiceMemImpl implements CaptchaCacheService { public class CaptchaCacheServiceMemImpl implements CaptchaCacheService {
@ -33,14 +36,14 @@ public class CaptchaCacheServiceMemImpl implements CaptchaCacheService {
return CacheUtil.get(key); return CacheUtil.get(key);
} }
@Override @Override
public Long increment(String key, long val) { public Long increment(String key, long val) {
Long ret = Long.valueOf(CacheUtil.get(key))+val; Long ret = Long.parseLong(Objects.requireNonNull(CacheUtil.get(key))) + val;
CacheUtil.set(key,ret+"",0); CacheUtil.set(key, ret + "", 0);
return ret; return ret;
} }
@Override @Override
public String type() { public String type() {
return "local"; return "local";
} }

View File

@ -3,8 +3,7 @@ package com.anji.captcha.service.impl;
import com.anji.captcha.model.common.Const; import com.anji.captcha.model.common.Const;
import com.anji.captcha.service.CaptchaCacheService; import com.anji.captcha.service.CaptchaCacheService;
import com.anji.captcha.service.CaptchaService; import com.anji.captcha.service.CaptchaService;
import org.slf4j.Logger; import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -14,10 +13,9 @@ import java.util.ServiceLoader;
/** /**
* Created by raodeming on 2020/5/26. * Created by raodeming on 2020/5/26.
*/ */
@Slf4j
public class CaptchaServiceFactory { public class CaptchaServiceFactory {
private static Logger logger = LoggerFactory.getLogger(CaptchaServiceFactory.class);
public static CaptchaService getInstance(Properties config) { public static CaptchaService getInstance(Properties config) {
//先把所有CaptchaService初始化通过init方法实例字体等add by lide1202@hotmail.com //先把所有CaptchaService初始化通过init方法实例字体等add by lide1202@hotmail.com
/*try{ /*try{
@ -41,20 +39,20 @@ public class CaptchaServiceFactory {
return cacheService.get(cacheType); return cacheService.get(cacheType);
} }
public volatile static Map<String, CaptchaService> instances = new HashMap(); public volatile static Map<String, CaptchaService> instances = new HashMap<>();
public volatile static Map<String, CaptchaCacheService> cacheService = new HashMap(); public volatile static Map<String, CaptchaCacheService> cacheService = new HashMap<>();
static { static {
ServiceLoader<CaptchaCacheService> cacheServices = ServiceLoader.load(CaptchaCacheService.class); ServiceLoader<CaptchaCacheService> cacheServices = ServiceLoader.load(CaptchaCacheService.class);
for (CaptchaCacheService item : cacheServices) { for (CaptchaCacheService item : cacheServices) {
cacheService.put(item.type(), item); cacheService.put(item.type(), item);
} }
logger.info("supported-captchaCache-service:{}", cacheService.keySet().toString()); log.info("supported-captchaCache-service:{}", cacheService.keySet().toString());
ServiceLoader<CaptchaService> services = ServiceLoader.load(CaptchaService.class); ServiceLoader<CaptchaService> services = ServiceLoader.load(CaptchaService.class);
for (CaptchaService item : services) { for (CaptchaService item : services) {
instances.put(item.captchaType(), item); instances.put(item.captchaType(), item);
} }
; ;
logger.info("supported-captchaTypes-service:{}", instances.keySet().toString()); log.info("supported-captchaTypes-service:{}", instances.keySet().toString());
} }
} }

View File

@ -6,6 +6,7 @@
*/ */
package com.anji.captcha.service.impl; package com.anji.captcha.service.impl;
import cn.hutool.core.util.StrUtil;
import com.anji.captcha.model.common.CaptchaTypeEnum; import com.anji.captcha.model.common.CaptchaTypeEnum;
import com.anji.captcha.model.common.Const; import com.anji.captcha.model.common.Const;
import com.anji.captcha.model.common.RepCodeEnum; import com.anji.captcha.model.common.RepCodeEnum;
@ -13,6 +14,7 @@ import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO; import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.model.vo.PointVO; import com.anji.captcha.model.vo.PointVO;
import com.anji.captcha.util.*; import com.anji.captcha.util.*;
import lombok.extern.slf4j.Slf4j;
import java.awt.*; import java.awt.*;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
@ -25,6 +27,7 @@ import java.util.*;
* <p> * <p>
* Created by raodeming on 2019/12/25. * Created by raodeming on 2019/12/25.
*/ */
@Slf4j
public class ClickWordCaptchaServiceImpl extends AbstractCaptchaService { public class ClickWordCaptchaServiceImpl extends AbstractCaptchaService {
public static String HAN_ZI = "\u7684\u4e00\u4e86\u662f\u6211\u4e0d\u5728\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a\u7740\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u4f60\u90fd\u628a\u597d\u8fd8\u591a\u6ca1\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u4e94\u7b2c\u4f7f\u5199\u519b\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636\u8fde\u8f66\u91cd\u4fbf\u6597\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u65e5\u51b3\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be\u65e9\u8bba\u5417\u6839\u5171\u8ba9\u76f8\u7814\u4eca\u5176\u4e66\u5750\u63a5\u5e94\u5173\u4fe1\u89c9\u6b65\u53cd\u5904\u8bb0\u5c06\u5343\u627e\u4e89\u9886\u6216\u5e08\u7ed3\u5757\u8dd1\u8c01\u8349\u8d8a\u5b57\u52a0\u811a\u7d27\u7231\u7b49\u4e60\u9635\u6015\u6708\u9752\u534a\u706b\u6cd5\u9898\u5efa\u8d76\u4f4d\u5531\u6d77\u4e03\u5973\u4efb\u4ef6\u611f\u51c6\u5f20\u56e2\u5c4b\u79bb\u8272\u8138\u7247\u79d1\u5012\u775b\u5229\u4e16\u521a\u4e14\u7531\u9001\u5207\u661f\u5bfc\u665a\u8868\u591f\u6574\u8ba4\u54cd\u96ea\u6d41\u672a\u573a\u8be5\u5e76\u5e95\u6df1\u523b\u5e73\u4f1f\u5fd9\u63d0\u786e\u8fd1\u4eae\u8f7b\u8bb2\u519c\u53e4\u9ed1\u544a\u754c\u62c9\u540d\u5440\u571f\u6e05\u9633\u7167\u529e\u53f2\u6539\u5386\u8f6c\u753b\u9020\u5634\u6b64\u6cbb\u5317\u5fc5\u670d\u96e8\u7a7f\u5185\u8bc6\u9a8c\u4f20\u4e1a\u83dc\u722c\u7761\u5174\u5f62\u91cf\u54b1\u89c2\u82e6\u4f53\u4f17\u901a\u51b2\u5408\u7834\u53cb\u5ea6\u672f\u996d\u516c\u65c1\u623f\u6781\u5357\u67aa\u8bfb\u6c99\u5c81\u7ebf\u91ce\u575a\u7a7a\u6536\u7b97\u81f3\u653f\u57ce\u52b3\u843d\u94b1\u7279\u56f4\u5f1f\u80dc\u6559\u70ed\u5c55\u5305\u6b4c\u7c7b\u6e10\u5f3a\u6570\u4e61\u547c\u6027\u97f3\u7b54\u54e5\u9645\u65e7\u795e\u5ea7\u7ae0\u5e2e\u5566\u53d7\u7cfb\u4ee4\u8df3\u975e\u4f55\u725b\u53d6\u5165\u5cb8\u6562\u6389\u5ffd\u79cd\u88c5\u9876\u6025\u6797\u505c\u606f\u53e5\u533a\u8863\u822c\u62a5\u53f6\u538b\u6162\u53d4\u80cc\u7ec6"; public static String HAN_ZI = "\u7684\u4e00\u4e86\u662f\u6211\u4e0d\u5728\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a\u7740\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u4f60\u90fd\u628a\u597d\u8fd8\u591a\u6ca1\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u4e94\u7b2c\u4f7f\u5199\u519b\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636\u8fde\u8f66\u91cd\u4fbf\u6597\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u65e5\u51b3\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be\u65e9\u8bba\u5417\u6839\u5171\u8ba9\u76f8\u7814\u4eca\u5176\u4e66\u5750\u63a5\u5e94\u5173\u4fe1\u89c9\u6b65\u53cd\u5904\u8bb0\u5c06\u5343\u627e\u4e89\u9886\u6216\u5e08\u7ed3\u5757\u8dd1\u8c01\u8349\u8d8a\u5b57\u52a0\u811a\u7d27\u7231\u7b49\u4e60\u9635\u6015\u6708\u9752\u534a\u706b\u6cd5\u9898\u5efa\u8d76\u4f4d\u5531\u6d77\u4e03\u5973\u4efb\u4ef6\u611f\u51c6\u5f20\u56e2\u5c4b\u79bb\u8272\u8138\u7247\u79d1\u5012\u775b\u5229\u4e16\u521a\u4e14\u7531\u9001\u5207\u661f\u5bfc\u665a\u8868\u591f\u6574\u8ba4\u54cd\u96ea\u6d41\u672a\u573a\u8be5\u5e76\u5e95\u6df1\u523b\u5e73\u4f1f\u5fd9\u63d0\u786e\u8fd1\u4eae\u8f7b\u8bb2\u519c\u53e4\u9ed1\u544a\u754c\u62c9\u540d\u5440\u571f\u6e05\u9633\u7167\u529e\u53f2\u6539\u5386\u8f6c\u753b\u9020\u5634\u6b64\u6cbb\u5317\u5fc5\u670d\u96e8\u7a7f\u5185\u8bc6\u9a8c\u4f20\u4e1a\u83dc\u722c\u7761\u5174\u5f62\u91cf\u54b1\u89c2\u82e6\u4f53\u4f17\u901a\u51b2\u5408\u7834\u53cb\u5ea6\u672f\u996d\u516c\u65c1\u623f\u6781\u5357\u67aa\u8bfb\u6c99\u5c81\u7ebf\u91ce\u575a\u7a7a\u6536\u7b97\u81f3\u653f\u57ce\u52b3\u843d\u94b1\u7279\u56f4\u5f1f\u80dc\u6559\u70ed\u5c55\u5305\u6b4c\u7c7b\u6e10\u5f3a\u6570\u4e61\u547c\u6027\u97f3\u7b54\u54e5\u9645\u65e7\u795e\u5ea7\u7ae0\u5e2e\u5566\u53d7\u7cfb\u4ee4\u8df3\u975e\u4f55\u725b\u53d6\u5165\u5cb8\u6562\u6389\u5ffd\u79cd\u88c5\u9876\u6025\u6797\u505c\u606f\u53e5\u533a\u8863\u822c\u62a5\u53f6\u538b\u6162\u53d4\u80cc\u7ec6";
@ -43,27 +46,27 @@ public class ClickWordCaptchaServiceImpl extends AbstractCaptchaService {
super.init(config); super.init(config);
clickWordFontStr = config.getProperty(Const.CAPTCHA_FONT_TYPE, "SourceHanSansCN-Normal.otf"); clickWordFontStr = config.getProperty(Const.CAPTCHA_FONT_TYPE, "SourceHanSansCN-Normal.otf");
try { try {
int size = Integer.valueOf(config.getProperty(Const.CAPTCHA_FONT_SIZE,HAN_ZI_SIZE+"")); int size = Integer.parseInt(config.getProperty(Const.CAPTCHA_FONT_SIZE,HAN_ZI_SIZE+""));
if (clickWordFontStr.toLowerCase().endsWith(".ttf") if (clickWordFontStr.toLowerCase().endsWith(".ttf")
|| clickWordFontStr.toLowerCase().endsWith(".ttc") || clickWordFontStr.toLowerCase().endsWith(".ttc")
|| clickWordFontStr.toLowerCase().endsWith(".otf")) { || clickWordFontStr.toLowerCase().endsWith(".otf")) {
this.clickWordFont = Font.createFont(Font.TRUETYPE_FONT, this.clickWordFont = Font.createFont(Font.TRUETYPE_FONT,
getClass().getResourceAsStream("/fonts/" + clickWordFontStr)) Objects.requireNonNull(getClass().getResourceAsStream("/fonts/" + clickWordFontStr)))
.deriveFont(Font.BOLD, size); .deriveFont(Font.BOLD, size);
} else { } else {
int style = Integer.valueOf(config.getProperty(Const.CAPTCHA_FONT_STYLE,Font.BOLD+"")); int style = Integer.parseInt(config.getProperty(Const.CAPTCHA_FONT_STYLE,Font.BOLD+""));
this.clickWordFont = new Font(clickWordFontStr, style, size); this.clickWordFont = new Font(clickWordFontStr, style, size);
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("load font error:{}", ex); log.error("load font error:{}", ex);
} }
this.wordTotalCount = Integer.valueOf(config.getProperty(Const.CAPTCHA_WORD_COUNT,"4")); this.wordTotalCount = Integer.parseInt(config.getProperty(Const.CAPTCHA_WORD_COUNT,"4"));
} }
@Override @Override
public void destroy(Properties config) { public void destroy(Properties config) {
logger.info("start-clear-history-data-", captchaType()); log.info("start-clear-history-data-", captchaType());
} }
@Override @Override
@ -74,12 +77,12 @@ public class ClickWordCaptchaServiceImpl extends AbstractCaptchaService {
} }
BufferedImage bufferedImage = ImageUtils.getPicClick(); BufferedImage bufferedImage = ImageUtils.getPicClick();
if (null == bufferedImage) { if (null == bufferedImage) {
logger.error("滑动底图未初始化成功,请检查路径"); log.error("滑动底图未初始化成功,请检查路径");
return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_BASEMAP_NULL); return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_BASEMAP_NULL);
} }
CaptchaVO imageData = getImageData(bufferedImage); CaptchaVO imageData = getImageData(bufferedImage);
if (imageData == null if (imageData == null
|| StringUtils.isBlank(imageData.getOriginalImageBase64())) { || StrUtil.isBlank(imageData.getOriginalImageBase64())) {
return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_ERROR); return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_ERROR);
} }
return ResponseModel.successData(imageData); return ResponseModel.successData(imageData);
@ -124,7 +127,7 @@ public class ClickWordCaptchaServiceImpl extends AbstractCaptchaService {
pointJson = decrypt(captchaVO.getPointJson(), point.get(0).getSecretKey()); pointJson = decrypt(captchaVO.getPointJson(), point.get(0).getSecretKey());
point1 = JsonUtil.parseArray(pointJson, PointVO.class); point1 = JsonUtil.parseArray(pointJson, PointVO.class);
} catch (Exception e) { } catch (Exception e) {
logger.error("验证码坐标解析失败", e); log.error("验证码坐标解析失败", e);
afterValidateFail(captchaVO); afterValidateFail(captchaVO);
return ResponseModel.errorMsg(e.getMessage()); return ResponseModel.errorMsg(e.getMessage());
} }
@ -143,7 +146,7 @@ public class ClickWordCaptchaServiceImpl extends AbstractCaptchaService {
try { try {
value = AESUtil.aesEncrypt(captchaVO.getToken().concat("---").concat(pointJson), secretKey); value = AESUtil.aesEncrypt(captchaVO.getToken().concat("---").concat(pointJson), secretKey);
} catch (Exception e) { } catch (Exception e) {
logger.error("AES加密失败", e); log.error("AES加密失败", e);
afterValidateFail(captchaVO); afterValidateFail(captchaVO);
return ResponseModel.errorMsg(e.getMessage()); return ResponseModel.errorMsg(e.getMessage());
} }
@ -159,7 +162,7 @@ public class ClickWordCaptchaServiceImpl extends AbstractCaptchaService {
/*if (captchaVO == null) { /*if (captchaVO == null) {
return RepCodeEnum.NULL_ERROR.parseError("captchaVO"); return RepCodeEnum.NULL_ERROR.parseError("captchaVO");
} }
if (StringUtils.isEmpty(captchaVO.getCaptchaVerification())) { if (StrUtil.isEmpty(captchaVO.getCaptchaVerification())) {
return RepCodeEnum.NULL_ERROR.parseError("captchaVerification"); return RepCodeEnum.NULL_ERROR.parseError("captchaVerification");
}*/ }*/
ResponseModel r = super.verification(captchaVO); ResponseModel r = super.verification(captchaVO);
@ -174,7 +177,7 @@ public class ClickWordCaptchaServiceImpl extends AbstractCaptchaService {
//二次校验取值后即刻失效 //二次校验取值后即刻失效
CaptchaServiceFactory.getCache(cacheType).delete(codeKey); CaptchaServiceFactory.getCache(cacheType).delete(codeKey);
} catch (Exception e) { } catch (Exception e) {
logger.error("验证码坐标解析失败", e); log.error("验证码坐标解析失败", e);
return ResponseModel.errorMsg(e.getMessage()); return ResponseModel.errorMsg(e.getMessage());
} }
return ResponseModel.success(); return ResponseModel.success();

View File

@ -6,17 +6,19 @@
*/ */
package com.anji.captcha.service.impl; package com.anji.captcha.service.impl;
import cn.hutool.core.util.StrUtil;
import com.anji.captcha.model.common.RepCodeEnum; import com.anji.captcha.model.common.RepCodeEnum;
import com.anji.captcha.model.common.ResponseModel; import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO; import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService; import com.anji.captcha.service.CaptchaService;
import com.anji.captcha.util.StringUtils; import lombok.extern.slf4j.Slf4j;
import java.util.Properties; import java.util.Properties;
/** /**
* Created by raodeming on 2019/12/25. * Created by raodeming on 2019/12/25.
*/ */
@Slf4j
public class DefaultCaptchaServiceImpl extends AbstractCaptchaService{ public class DefaultCaptchaServiceImpl extends AbstractCaptchaService{
@Override @Override
@ -53,7 +55,7 @@ public class DefaultCaptchaServiceImpl extends AbstractCaptchaService{
if (captchaVO == null) { if (captchaVO == null) {
return RepCodeEnum.NULL_ERROR.parseError("captchaVO"); return RepCodeEnum.NULL_ERROR.parseError("captchaVO");
} }
if (StringUtils.isEmpty(captchaVO.getCaptchaType())) { if (StrUtil.isEmpty(captchaVO.getCaptchaType())) {
return RepCodeEnum.NULL_ERROR.parseError("类型"); return RepCodeEnum.NULL_ERROR.parseError("类型");
} }
return getService(captchaVO.getCaptchaType()).get(captchaVO); return getService(captchaVO.getCaptchaType()).get(captchaVO);
@ -64,10 +66,10 @@ public class DefaultCaptchaServiceImpl extends AbstractCaptchaService{
if (captchaVO == null) { if (captchaVO == null) {
return RepCodeEnum.NULL_ERROR.parseError("captchaVO"); return RepCodeEnum.NULL_ERROR.parseError("captchaVO");
} }
if (StringUtils.isEmpty(captchaVO.getCaptchaType())) { if (StrUtil.isEmpty(captchaVO.getCaptchaType())) {
return RepCodeEnum.NULL_ERROR.parseError("类型"); return RepCodeEnum.NULL_ERROR.parseError("类型");
} }
if (StringUtils.isEmpty(captchaVO.getToken())) { if (StrUtil.isEmpty(captchaVO.getToken())) {
return RepCodeEnum.NULL_ERROR.parseError("token"); return RepCodeEnum.NULL_ERROR.parseError("token");
} }
return getService(captchaVO.getCaptchaType()).check(captchaVO); return getService(captchaVO.getCaptchaType()).check(captchaVO);
@ -78,7 +80,7 @@ public class DefaultCaptchaServiceImpl extends AbstractCaptchaService{
if (captchaVO == null) { if (captchaVO == null) {
return RepCodeEnum.NULL_ERROR.parseError("captchaVO"); return RepCodeEnum.NULL_ERROR.parseError("captchaVO");
} }
if (StringUtils.isEmpty(captchaVO.getCaptchaVerification())) { if (StrUtil.isEmpty(captchaVO.getCaptchaVerification())) {
return RepCodeEnum.NULL_ERROR.parseError("二次校验参数"); return RepCodeEnum.NULL_ERROR.parseError("二次校验参数");
} }
try { try {
@ -89,7 +91,7 @@ public class DefaultCaptchaServiceImpl extends AbstractCaptchaService{
//二次校验取值后即刻失效 //二次校验取值后即刻失效
CaptchaServiceFactory.getCache(cacheType).delete(codeKey); CaptchaServiceFactory.getCache(cacheType).delete(codeKey);
} catch (Exception e) { } catch (Exception e) {
logger.error("验证码坐标解析失败", e); log.error("验证码坐标解析失败", e);
return ResponseModel.errorMsg(e.getMessage()); return ResponseModel.errorMsg(e.getMessage());
} }
return ResponseModel.success(); return ResponseModel.success();

View File

@ -1,11 +1,11 @@
package com.anji.captcha.service.impl; package com.anji.captcha.service.impl;
import cn.hutool.core.util.StrUtil;
import com.anji.captcha.model.common.Const; import com.anji.captcha.model.common.Const;
import com.anji.captcha.model.common.RepCodeEnum; import com.anji.captcha.model.common.RepCodeEnum;
import com.anji.captcha.model.common.ResponseModel; import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO; import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaCacheService; import com.anji.captcha.service.CaptchaCacheService;
import com.anji.captcha.util.StringUtils;
import java.util.Objects; import java.util.Objects;
import java.util.Properties; import java.util.Properties;
@ -66,15 +66,15 @@ public interface FrequencyLimitHandler {
} }
private String getClientCId(CaptchaVO input, String type) { private String getClientCId(CaptchaVO input, String type) {
return String.format(LIMIT_KEY ,type,input.getClientUid()); return String.format(LIMIT_KEY, type, input.getClientUid());
} }
@Override @Override
public ResponseModel validateGet(CaptchaVO d) { public ResponseModel validateGet(CaptchaVO d) {
// 无客户端身份标识不限制 // 无客户端身份标识不限制
if(StringUtils.isEmpty(d.getClientUid())){ if (StrUtil.isEmpty(d.getClientUid())) {
return null; return null;
} }
String getKey = getClientCId(d, "GET"); String getKey = getClientCId(d, "GET");
String lockKey = getClientCId(d, "LOCK"); String lockKey = getClientCId(d, "LOCK");
// 失败次数过多锁定 // 失败次数过多锁定
@ -88,7 +88,7 @@ public interface FrequencyLimitHandler {
} }
cacheService.increment(getKey, 1); cacheService.increment(getKey, 1);
// 1分钟内请求次数过多 // 1分钟内请求次数过多
if (Long.valueOf(getCnts) > Long.parseLong(config.getProperty(Const.REQ_GET_MINUTE_LIMIT, "120"))) { if (Long.parseLong(getCnts) > Long.parseLong(config.getProperty(Const.REQ_GET_MINUTE_LIMIT, "120"))) {
return ResponseModel.errorMsg(RepCodeEnum.API_REQ_LIMIT_GET_ERROR); return ResponseModel.errorMsg(RepCodeEnum.API_REQ_LIMIT_GET_ERROR);
} }
@ -100,9 +100,9 @@ public interface FrequencyLimitHandler {
return null; return null;
} }
// 1分钟内失败5次 // 1分钟内失败5次
if (Long.valueOf(failCnts) > Long.parseLong(config.getProperty(Const.REQ_GET_LOCK_LIMIT, "5"))) { if (Long.parseLong(failCnts) > Long.parseLong(config.getProperty(Const.REQ_GET_LOCK_LIMIT, "5"))) {
// get接口锁定5分钟 // get接口锁定5分钟
cacheService.set(lockKey, "1", Long.valueOf(config.getProperty(Const.REQ_GET_LOCK_SECONDS, "300"))); cacheService.set(lockKey, "1", Long.parseLong(config.getProperty(Const.REQ_GET_LOCK_SECONDS, "300")));
return ResponseModel.errorMsg(RepCodeEnum.API_REQ_LOCK_GET_ERROR); return ResponseModel.errorMsg(RepCodeEnum.API_REQ_LOCK_GET_ERROR);
} }
return null; return null;
@ -110,10 +110,10 @@ public interface FrequencyLimitHandler {
@Override @Override
public ResponseModel validateCheck(CaptchaVO d) { public ResponseModel validateCheck(CaptchaVO d) {
// 无客户端身份标识不限制 // 无客户端身份标识不限制
if(StringUtils.isEmpty(d.getClientUid())){ if (StrUtil.isEmpty(d.getClientUid())) {
return null; return null;
} }
/*String getKey = getClientCId(d, "GET"); /*String getKey = getClientCId(d, "GET");
if(Objects.isNull(cacheService.get(getKey))){ if(Objects.isNull(cacheService.get(getKey))){
return ResponseModel.errorMsg(RepCodeEnum.API_REQ_INVALID); return ResponseModel.errorMsg(RepCodeEnum.API_REQ_INVALID);
@ -125,7 +125,7 @@ public interface FrequencyLimitHandler {
v = "1"; v = "1";
} }
cacheService.increment(key, 1); cacheService.increment(key, 1);
if (Long.valueOf(v) > Long.valueOf(config.getProperty(Const.REQ_CHECK_MINUTE_LIMIT, "600"))) { if (Long.parseLong(v) > Long.parseLong(config.getProperty(Const.REQ_CHECK_MINUTE_LIMIT, "600"))) {
return ResponseModel.errorMsg(RepCodeEnum.API_REQ_LIMIT_CHECK_ERROR); return ResponseModel.errorMsg(RepCodeEnum.API_REQ_LIMIT_CHECK_ERROR);
} }
return null; return null;
@ -144,7 +144,7 @@ public interface FrequencyLimitHandler {
v = "1"; v = "1";
} }
cacheService.increment(key, 1); cacheService.increment(key, 1);
if (Long.valueOf(v) > Long.valueOf(config.getProperty(Const.REQ_VALIDATE_MINUTE_LIMIT, "600"))) { if (Long.parseLong(v) > Long.parseLong(config.getProperty(Const.REQ_VALIDATE_MINUTE_LIMIT, "600"))) {
return ResponseModel.errorMsg(RepCodeEnum.API_REQ_LIMIT_VERIFY_ERROR); return ResponseModel.errorMsg(RepCodeEnum.API_REQ_LIMIT_VERIFY_ERROR);
} }
return null; return null;

View File

@ -7,10 +7,13 @@
package com.anji.captcha.util; package com.anji.captcha.util;
import cn.hutool.core.util.StrUtil;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.KeyGenerator; import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.Base64; import java.util.Base64;
@ -20,6 +23,7 @@ public class AESUtil {
/** /**
* 获取随机key * 获取随机key
*
* @return * @return
*/ */
public static String getKey() { public static String getKey() {
@ -29,39 +33,43 @@ public class AESUtil {
/** /**
* 将byte[]转为各种进制的字符串 * 将byte[]转为各种进制的字符串
*
* @param bytes byte[] * @param bytes byte[]
* @param radix 可以转换进制的范围从Character.MIN_RADIX到Character.MAX_RADIX超出范围后变为10进制 * @param radix 可以转换进制的范围从Character.MIN_RADIX到Character.MAX_RADIX超出范围后变为10进制
* @return 转换后的字符串 * @return 转换后的字符串
*/ */
public static String binary(byte[] bytes, int radix){ public static String binary(byte[] bytes, int radix) {
return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数 return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数
} }
/** /**
* base 64 encode * base 64 encode
*
* @param bytes 待编码的byte[] * @param bytes 待编码的byte[]
* @return 编码后的base 64 code * @return 编码后的base 64 code
*/ */
public static String base64Encode(byte[] bytes){ public static String base64Encode(byte[] bytes) {
//return Base64.encodeBase64String(bytes); //return Base64.encodeBase64String(bytes);
return Base64.getEncoder().encodeToString(bytes); return Base64.getEncoder().encodeToString(bytes);
} }
/** /**
* base 64 decode * base 64 decode
*
* @param base64Code 待解码的base 64 code * @param base64Code 待解码的base 64 code
* @return 解码后的byte[] * @return 解码后的byte[]
* @throws Exception * @throws Exception
*/ */
public static byte[] base64Decode(String base64Code) throws Exception{ public static byte[] base64Decode(String base64Code) throws Exception {
Base64.Decoder decoder = Base64.getDecoder(); Base64.Decoder decoder = Base64.getDecoder();
return StringUtils.isEmpty(base64Code) ? null : decoder.decode(base64Code); return StrUtil.isEmpty(base64Code) ? null : decoder.decode(base64Code);
} }
/** /**
* AES加密 * AES加密
* @param content 待加密的内容 *
* @param content 待加密的内容
* @param encryptKey 加密密钥 * @param encryptKey 加密密钥
* @return 加密后的byte[] * @return 加密后的byte[]
* @throws Exception * @throws Exception
@ -72,19 +80,20 @@ public class AESUtil {
Cipher cipher = Cipher.getInstance(ALGORITHMSTR); Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES")); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));
return cipher.doFinal(content.getBytes("utf-8")); return cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
} }
/** /**
* AES加密为base 64 code * AES加密为base 64 code
* @param content 待加密的内容 *
* @param content 待加密的内容
* @param encryptKey 加密密钥 * @param encryptKey 加密密钥
* @return 加密后的base 64 code * @return 加密后的base 64 code
* @throws Exception * @throws Exception
*/ */
public static String aesEncrypt(String content, String encryptKey) throws Exception { public static String aesEncrypt(String content, String encryptKey) throws Exception {
if (StringUtils.isBlank(encryptKey)) { if (StrUtil.isBlank(encryptKey)) {
return content; return content;
} }
return base64Encode(aesEncryptToBytes(content, encryptKey)); return base64Encode(aesEncryptToBytes(content, encryptKey));
@ -92,8 +101,9 @@ public class AESUtil {
/** /**
* AES解密 * AES解密
*
* @param encryptBytes 待解密的byte[] * @param encryptBytes 待解密的byte[]
* @param decryptKey 解密密钥 * @param decryptKey 解密密钥
* @return 解密后的String * @return 解密后的String
* @throws Exception * @throws Exception
*/ */
@ -110,16 +120,17 @@ public class AESUtil {
/** /**
* 将base 64 code AES解密 * 将base 64 code AES解密
*
* @param encryptStr 待解密的base 64 code * @param encryptStr 待解密的base 64 code
* @param decryptKey 解密密钥 * @param decryptKey 解密密钥
* @return 解密后的string * @return 解密后的string
* @throws Exception * @throws Exception
*/ */
public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception { public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {
if (StringUtils.isBlank(decryptKey)) { if (StrUtil.isBlank(decryptKey)) {
return encryptStr; return encryptStr;
} }
return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey); return StrUtil.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
} }
/** /**

View File

@ -1,53 +0,0 @@
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.anji.captcha.util;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public abstract class Base64Utils {
private static final Charset DEFAULT_CHARSET;
public Base64Utils() {
}
public static byte[] encode(byte[] src) {
return src.length == 0 ? src : Base64.getEncoder().encode(src);
}
public static byte[] decode(byte[] src) {
return src.length == 0 ? src : Base64.getDecoder().decode(src);
}
public static byte[] encodeUrlSafe(byte[] src) {
return src.length == 0 ? src : Base64.getUrlEncoder().encode(src);
}
public static byte[] decodeUrlSafe(byte[] src) {
return src.length == 0 ? src : Base64.getUrlDecoder().decode(src);
}
public static String encodeToString(byte[] src) {
return src.length == 0 ? "" : new String(encode(src), DEFAULT_CHARSET);
}
public static byte[] decodeFromString(String src) {
return src.isEmpty() ? new byte[0] : decode(src.getBytes(DEFAULT_CHARSET));
}
public static String encodeToUrlSafeString(byte[] src) {
return new String(encodeUrlSafe(src), DEFAULT_CHARSET);
}
public static byte[] decodeFromUrlSafeString(String src) {
return decodeUrlSafe(src.getBytes(DEFAULT_CHARSET));
}
static {
DEFAULT_CHARSET = StandardCharsets.UTF_8;
}
}

View File

@ -24,8 +24,9 @@ public final class CacheUtil {
/** /**
* 初始化 * 初始化
*
* @param cacheMaxNumber 缓存最大个数 * @param cacheMaxNumber 缓存最大个数
* @param second 定时任务 秒执行清除过期缓存 * @param second 定时任务 秒执行清除过期缓存
*/ */
public static void init(int cacheMaxNumber, long second) { public static void init(int cacheMaxNumber, long second) {
CACHE_MAX_NUMBER = cacheMaxNumber; CACHE_MAX_NUMBER = cacheMaxNumber;
@ -37,27 +38,25 @@ public final class CacheUtil {
refresh(); refresh();
} }
}, 0, second * 1000);*/ }, 0, second * 1000);*/
scheduledExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactory() { ScheduledExecutorService scheduledExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
@Override @Override
public Thread newThread(Runnable r) { public Thread newThread(Runnable r) {
return new Thread(r,"thd-captcha-cache-clean"); return new Thread(r, "thd-captcha-cache-clean");
} }
},new ThreadPoolExecutor.CallerRunsPolicy()); }, new ThreadPoolExecutor.CallerRunsPolicy());
scheduledExecutor.scheduleAtFixedRate(new Runnable() { scheduledExecutor.scheduleAtFixedRate(new Runnable() {
@Override @Override
public void run() { public void run() {
refresh(); refresh();
} }
},10,second,TimeUnit.SECONDS); }, 10, second, TimeUnit.SECONDS);
} }
} }
private static ScheduledExecutorService scheduledExecutor;
/** /**
* 缓存刷新,清除过期数据 * 缓存刷新,清除过期数据
*/ */
public static void refresh(){ public static void refresh() {
logger.debug("local缓存刷新,清除过期数据"); logger.debug("local缓存刷新,清除过期数据");
for (String key : CACHE_MAP.keySet()) { for (String key : CACHE_MAP.keySet()) {
exists(key); exists(key);
@ -65,24 +64,24 @@ public final class CacheUtil {
} }
public static void set(String key, String value, long expiresInSeconds){ public static void set(String key, String value, long expiresInSeconds) {
//设置阈值达到即clear缓存 //设置阈值达到即clear缓存
if (CACHE_MAP.size() > CACHE_MAX_NUMBER * 2) { if (CACHE_MAP.size() > CACHE_MAX_NUMBER * 2) {
logger.info("CACHE_MAP达到阈值clear map"); logger.info("CACHE_MAP达到阈值clear map");
clear(); clear();
} }
CACHE_MAP.put(key, value); CACHE_MAP.put(key, value);
if(expiresInSeconds >0) { if (expiresInSeconds > 0) {
CACHE_MAP.put(key + "_HoldTime", System.currentTimeMillis() + expiresInSeconds * 1000);//缓存失效时间 CACHE_MAP.put(key + "_HoldTime", System.currentTimeMillis() + expiresInSeconds * 1000);//缓存失效时间
} }
} }
public static void delete(String key){ public static void delete(String key) {
CACHE_MAP.remove(key); CACHE_MAP.remove(key);
CACHE_MAP.remove(key + "_HoldTime"); CACHE_MAP.remove(key + "_HoldTime");
} }
public static boolean exists(String key){ public static boolean exists(String key) {
Long cacheHoldTime = (Long) CACHE_MAP.get(key + "_HoldTime"); Long cacheHoldTime = (Long) CACHE_MAP.get(key + "_HoldTime");
if (cacheHoldTime == null || cacheHoldTime == 0L) { if (cacheHoldTime == null || cacheHoldTime == 0L) {
return false; return false;
@ -95,9 +94,9 @@ public final class CacheUtil {
} }
public static String get(String key){ public static String get(String key) {
if (exists(key)) { if (exists(key)) {
return (String)CACHE_MAP.get(key); return (String) CACHE_MAP.get(key);
} }
return null; return null;
} }

View File

@ -18,7 +18,7 @@ public abstract class FileCopyUtils {
} }
public static void copy(byte[] in, File out) throws IOException { public static void copy(byte[] in, File out) throws IOException {
copy((InputStream)(new ByteArrayInputStream(in)), (OutputStream)Files.newOutputStream(out.toPath())); copy((InputStream) (new ByteArrayInputStream(in)), (OutputStream) Files.newOutputStream(out.toPath()));
} }
public static byte[] copyToByteArray(File in) throws IOException { public static byte[] copyToByteArray(File in) throws IOException {
@ -63,7 +63,7 @@ public abstract class FileCopyUtils {
return new byte[0]; return new byte[0];
} else { } else {
ByteArrayOutputStream out = new ByteArrayOutputStream(4096); ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
copy((InputStream)in, (OutputStream)out); copy((InputStream) in, (OutputStream) out);
return out.toByteArray(); return out.toByteArray();
} }
} }
@ -74,13 +74,12 @@ public abstract class FileCopyUtils {
char[] buffer = new char[4096]; char[] buffer = new char[4096];
int bytesRead; int bytesRead;
for(boolean var4 = true; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) { for (boolean var4 = true; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) {
out.write(buffer, 0, bytesRead); out.write(buffer, 0, bytesRead);
} }
out.flush(); out.flush();
int var5 = byteCount; return byteCount;
return var5;
} finally { } finally {
try { try {
in.close(); in.close();
@ -113,7 +112,7 @@ public abstract class FileCopyUtils {
return ""; return "";
} else { } else {
StringWriter out = new StringWriter(); StringWriter out = new StringWriter();
copy((Reader)in, (Writer)out); copy((Reader) in, (Writer) out);
return out.toString(); return out.toString();
} }
} }

View File

@ -6,9 +6,10 @@
*/ */
package com.anji.captcha.util; package com.anji.captcha.util;
import cn.hutool.core.util.StrUtil;
import com.anji.captcha.model.common.CaptchaBaseMapEnum; import com.anji.captcha.model.common.CaptchaBaseMapEnum;
import org.slf4j.Logger; import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory; import org.springframework.util.Base64Utils;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@ -19,9 +20,8 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@Slf4j
public class ImageUtils { public class ImageUtils {
private static Logger logger = LoggerFactory.getLogger(ImageUtils.class);
private static Map<String, String> originalCacheMap = new ConcurrentHashMap(); //滑块底图 private static Map<String, String> originalCacheMap = new ConcurrentHashMap(); //滑块底图
private static Map<String, String> slidingBlockCacheMap = new ConcurrentHashMap(); //滑块 private static Map<String, String> slidingBlockCacheMap = new ConcurrentHashMap(); //滑块
private static Map<String, String> picClickCacheMap = new ConcurrentHashMap(); //点选文字 private static Map<String, String> picClickCacheMap = new ConcurrentHashMap(); //点选文字
@ -29,7 +29,7 @@ public class ImageUtils {
public static void cacheImage(String captchaOriginalPathJigsaw, String captchaOriginalPathClick) { public static void cacheImage(String captchaOriginalPathJigsaw, String captchaOriginalPathClick) {
//滑动拼图 //滑动拼图
if (StringUtils.isBlank(captchaOriginalPathJigsaw)) { if (StrUtil.isBlank(captchaOriginalPathJigsaw)) {
originalCacheMap.putAll(getResourcesImagesFile("defaultImages/jigsaw/original")); originalCacheMap.putAll(getResourcesImagesFile("defaultImages/jigsaw/original"));
slidingBlockCacheMap.putAll(getResourcesImagesFile("defaultImages/jigsaw/slidingBlock")); slidingBlockCacheMap.putAll(getResourcesImagesFile("defaultImages/jigsaw/slidingBlock"));
} else { } else {
@ -37,7 +37,7 @@ public class ImageUtils {
slidingBlockCacheMap.putAll(getImagesFile(captchaOriginalPathJigsaw + File.separator + "slidingBlock")); slidingBlockCacheMap.putAll(getImagesFile(captchaOriginalPathJigsaw + File.separator + "slidingBlock"));
} }
//点选文字 //点选文字
if (StringUtils.isBlank(captchaOriginalPathClick)) { if (StrUtil.isBlank(captchaOriginalPathClick)) {
picClickCacheMap.putAll(getResourcesImagesFile("defaultImages/pic-click")); picClickCacheMap.putAll(getResourcesImagesFile("defaultImages/pic-click"));
} else { } else {
picClickCacheMap.putAll(getImagesFile(captchaOriginalPathClick)); picClickCacheMap.putAll(getImagesFile(captchaOriginalPathClick));
@ -45,7 +45,7 @@ public class ImageUtils {
fileNameMap.put(CaptchaBaseMapEnum.ORIGINAL.getCodeValue(), originalCacheMap.keySet().toArray(new String[0])); fileNameMap.put(CaptchaBaseMapEnum.ORIGINAL.getCodeValue(), originalCacheMap.keySet().toArray(new String[0]));
fileNameMap.put(CaptchaBaseMapEnum.SLIDING_BLOCK.getCodeValue(), slidingBlockCacheMap.keySet().toArray(new String[0])); fileNameMap.put(CaptchaBaseMapEnum.SLIDING_BLOCK.getCodeValue(), slidingBlockCacheMap.keySet().toArray(new String[0]));
fileNameMap.put(CaptchaBaseMapEnum.PIC_CLICK.getCodeValue(), picClickCacheMap.keySet().toArray(new String[0])); fileNameMap.put(CaptchaBaseMapEnum.PIC_CLICK.getCodeValue(), picClickCacheMap.keySet().toArray(new String[0]));
logger.info("初始化底图:{}", JsonUtil.toJSONString(fileNameMap)); log.info("初始化底图:{}", JsonUtil.toJSONString(fileNameMap));
} }
public static void cacheBootImage(Map<String, String> originalMap, Map<String, String> slidingBlockMap, Map<String, String> picClickMap) { public static void cacheBootImage(Map<String, String> originalMap, Map<String, String> slidingBlockMap, Map<String, String> picClickMap) {
@ -55,7 +55,7 @@ public class ImageUtils {
fileNameMap.put(CaptchaBaseMapEnum.ORIGINAL.getCodeValue(), originalCacheMap.keySet().toArray(new String[0])); fileNameMap.put(CaptchaBaseMapEnum.ORIGINAL.getCodeValue(), originalCacheMap.keySet().toArray(new String[0]));
fileNameMap.put(CaptchaBaseMapEnum.SLIDING_BLOCK.getCodeValue(), slidingBlockCacheMap.keySet().toArray(new String[0])); fileNameMap.put(CaptchaBaseMapEnum.SLIDING_BLOCK.getCodeValue(), slidingBlockCacheMap.keySet().toArray(new String[0]));
fileNameMap.put(CaptchaBaseMapEnum.PIC_CLICK.getCodeValue(), picClickCacheMap.keySet().toArray(new String[0])); fileNameMap.put(CaptchaBaseMapEnum.PIC_CLICK.getCodeValue(), picClickCacheMap.keySet().toArray(new String[0]));
logger.info("自定义resource底图:{}", JsonUtil.toJSONString(fileNameMap)); log.info("自定义resource底图:{}", JsonUtil.toJSONString(fileNameMap));
} }
@ -75,8 +75,7 @@ public class ImageUtils {
return null; return null;
} }
Integer randomInt = RandomUtils.getRandomInt(0, strings.length); Integer randomInt = RandomUtils.getRandomInt(0, strings.length);
String s = slidingBlockCacheMap.get(strings[randomInt]); return slidingBlockCacheMap.get(strings[randomInt]);
return s;
} }
public static BufferedImage getPicClick() { public static BufferedImage getPicClick() {
@ -160,8 +159,6 @@ public class ImageUtils {
byte[] bytes = FileCopyUtils.copyToByteArray(fileInputStream); byte[] bytes = FileCopyUtils.copyToByteArray(fileInputStream);
String string = Base64Utils.encodeToString(bytes); String string = Base64Utils.encodeToString(bytes);
imgMap.put(item.getName(), string); imgMap.put(item.getName(), string);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@ -1,8 +1,7 @@
package com.anji.captcha.util; package com.anji.captcha.util;
import com.anji.captcha.model.vo.PointVO; import com.anji.captcha.model.vo.PointVO;
import org.slf4j.Logger; import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -12,63 +11,63 @@ import java.util.Map;
* 替换掉fastjson自定义实现相关方法 * 替换掉fastjson自定义实现相关方法
* note: 该实现不具有通用性仅用于本项目 * note: 该实现不具有通用性仅用于本项目
* *
*@author WongBin * @author WongBin
*@date 2021/1/8 * @date 2021/1/8
*/ */
@Slf4j
public class JsonUtil { public class JsonUtil {
private static Logger logger = LoggerFactory.getLogger(JsonUtil.class); public static List<PointVO> parseArray(String text, Class<PointVO> clazz) {
public static List<PointVO> parseArray(String text, Class<PointVO> clazz) { if (text == null) {
if (text == null) { return null;
return null; } else {
} else { String[] arr = text.replaceFirst("\\[", "")
String[] arr = text.replaceFirst("\\[","") .replaceFirst("\\]", "").split("\\}");
.replaceFirst("\\]","").split("\\}"); List<PointVO> ret = new ArrayList<>(arr.length);
List<PointVO> ret = new ArrayList<>(arr.length); for (String s : arr) {
for (String s : arr) { ret.add(parseObject(s, PointVO.class));
ret.add(parseObject(s,PointVO.class)); }
} return ret;
return ret; }
} }
}
public static PointVO parseObject(String text, Class<PointVO> clazz) { public static PointVO parseObject(String text, Class<PointVO> clazz) {
if(text == null) { if (text == null) {
return null; return null;
} }
/*if(!clazz.isAssignableFrom(PointVO.class)) { /*if(!clazz.isAssignableFrom(PointVO.class)) {
throw new UnsupportedOperationException("不支持的输入类型:" throw new UnsupportedOperationException("不支持的输入类型:"
+ clazz.getSimpleName()); + clazz.getSimpleName());
}*/ }*/
try { try {
PointVO ret = clazz.newInstance(); PointVO ret = clazz.newInstance();
return ret.parse(text); return ret.parse(text);
}catch (Exception ex){ } catch (Exception ex) {
logger.error("json解析异常", ex); log.error("json解析异常", ex);
} }
return null; return null;
} }
public static String toJSONString(Object object) { public static String toJSONString(Object object) {
if(object == null) { if (object == null) {
return "{}"; return "{}";
} }
if(object instanceof PointVO){ if (object instanceof PointVO) {
PointVO t = (PointVO)object; PointVO t = (PointVO) object;
return t.toJsonString(); return t.toJsonString();
} }
if(object instanceof List){ if (object instanceof List) {
List<PointVO> list = (List<PointVO>)object; List<PointVO> list = (List<PointVO>) object;
StringBuilder buf = new StringBuilder("["); StringBuilder buf = new StringBuilder("[");
list.stream().forEach(t->{ list.forEach(t -> {
buf.append(t.toJsonString()).append(","); buf.append(t.toJsonString()).append(",");
}); });
return buf.deleteCharAt(buf.lastIndexOf(",")).append("]").toString(); return buf.deleteCharAt(buf.lastIndexOf(",")).append("]").toString();
} }
if(object instanceof Map){ if (object instanceof Map) {
return ((Map)object).entrySet().toString(); return ((Map) object).entrySet().toString();
} }
throw new UnsupportedOperationException("不支持的输入类型:" throw new UnsupportedOperationException("不支持的输入类型:"
+object.getClass().getSimpleName()); + object.getClass().getSimpleName());
} }
} }

View File

@ -1,5 +1,6 @@
package com.anji.captcha.util; package com.anji.captcha.util;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest; import java.security.MessageDigest;
/** /**
@ -8,17 +9,18 @@ import java.security.MessageDigest;
public abstract class MD5Util { public abstract class MD5Util {
/** /**
* 获取指定字符串的md5值 * 获取指定字符串的md5值
*
* @param dataStr 明文 * @param dataStr 明文
* @return String * @return String
*/ */
public static String md5(String dataStr) { public static String md5(String dataStr) {
try { try {
MessageDigest m = MessageDigest.getInstance("MD5"); MessageDigest m = MessageDigest.getInstance("MD5");
m.update(dataStr.getBytes("UTF8")); m.update(dataStr.getBytes(StandardCharsets.UTF_8));
byte[] s = m.digest(); byte[] s = m.digest();
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
for (int i = 0; i < s.length; i++) { for (byte b : s) {
result.append(Integer.toHexString((0x000000FF & s[i]) | 0xFFFFFF00).substring(6)); result.append(Integer.toHexString((0x000000FF & b) | 0xFFFFFF00).substring(6));
} }
return result.toString(); return result.toString();
} catch (Exception e) { } catch (Exception e) {
@ -29,10 +31,11 @@ public abstract class MD5Util {
/** /**
* 获取指定字符串的md5值, md5(str+salt) * 获取指定字符串的md5值, md5(str+salt)
*
* @param dataStr 明文 * @param dataStr 明文
* @return String * @return String
*/ */
public static String md5WithSalt(String dataStr,String salt) { public static String md5WithSalt(String dataStr, String salt) {
return md5(dataStr + salt); return md5(dataStr + salt);
} }

View File

@ -31,11 +31,10 @@ public class RandomUtils {
* @return * @return
*/ */
public static String getRandomHan(String hanZi) { public static String getRandomHan(String hanZi) {
String ch = hanZi.charAt(new Random().nextInt(hanZi.length())) + ""; return hanZi.charAt(new Random().nextInt(hanZi.length())) + "";
return ch;
} }
public static int getRandomInt(int bound){ public static int getRandomInt(int bound) {
return ThreadLocalRandom.current().nextInt(bound); return ThreadLocalRandom.current().nextInt(bound);
} }
@ -68,25 +67,27 @@ public class RandomUtils {
/** /**
* 随机范围内数字 * 随机范围内数字
*
* @param startNum * @param startNum
* @param endNum * @param endNum
* @return * @return
*/ */
public static Integer getRandomInt(int startNum, int endNum) { public static Integer getRandomInt(int startNum, int endNum) {
return ThreadLocalRandom.current().nextInt(endNum-startNum) + startNum; return ThreadLocalRandom.current().nextInt(endNum - startNum) + startNum;
} }
/** /**
* 获取随机字符串 * 获取随机字符串
*
* @param length * @param length
* @return * @return
*/ */
public static String getRandomString(int length){ public static String getRandomString(int length) {
String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random=new Random(); Random random = new Random();
StringBuffer sb=new StringBuffer(); StringBuffer sb = new StringBuffer();
for(int i=0;i<length;i++){ for (int i = 0; i < length; i++) {
int number=random.nextInt(62); int number = random.nextInt(62);
sb.append(str.charAt(number)); sb.append(str.charAt(number));
} }
return sb.toString(); return sb.toString();

View File

@ -19,7 +19,7 @@ public abstract class StreamUtils {
return new byte[0]; return new byte[0];
} else { } else {
ByteArrayOutputStream out = new ByteArrayOutputStream(4096); ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
copy((InputStream)in, out); copy((InputStream) in, out);
return out.toByteArray(); return out.toByteArray();
} }
} }
@ -31,10 +31,9 @@ public abstract class StreamUtils {
StringBuilder out = new StringBuilder(); StringBuilder out = new StringBuilder();
InputStreamReader reader = new InputStreamReader(in, charset); InputStreamReader reader = new InputStreamReader(in, charset);
char[] buffer = new char[4096]; char[] buffer = new char[4096];
boolean var5 = true;
int bytesRead; int bytesRead;
while((bytesRead = reader.read(buffer)) != -1) { while ((bytesRead = reader.read(buffer)) != -1) {
out.append(buffer, 0, bytesRead); out.append(buffer, 0, bytesRead);
} }
@ -57,7 +56,7 @@ public abstract class StreamUtils {
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
int bytesRead; int bytesRead;
for(boolean var4 = true; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) { for (boolean var4 = true; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) {
out.write(buffer, 0, bytesRead); out.write(buffer, 0, bytesRead);
} }
@ -73,17 +72,17 @@ public abstract class StreamUtils {
long bytesToCopy = end - start + 1L; long bytesToCopy = end - start + 1L;
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
while(bytesToCopy > 0L) { while (bytesToCopy > 0L) {
int bytesRead = in.read(buffer); int bytesRead = in.read(buffer);
if (bytesRead == -1) { if (bytesRead == -1) {
break; break;
} }
if ((long)bytesRead <= bytesToCopy) { if ((long) bytesRead <= bytesToCopy) {
out.write(buffer, 0, bytesRead); out.write(buffer, 0, bytesRead);
bytesToCopy -= (long)bytesRead; bytesToCopy -= (long) bytesRead;
} else { } else {
out.write(buffer, 0, (int)bytesToCopy); out.write(buffer, 0, (int) bytesToCopy);
bytesToCopy = 0L; bytesToCopy = 0L;
} }
} }
@ -96,7 +95,7 @@ public abstract class StreamUtils {
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
int byteCount; int byteCount;
int bytesRead; int bytesRead;
for(byteCount = 0; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) { for (byteCount = 0; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) {
} }
return byteCount; return byteCount;

View File

@ -1,8 +1,10 @@
package cn.iocoder.yudao.module.system.controller.admin.captcha; package cn.iocoder.yudao.module.system.controller.admin.captcha;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import com.anji.captcha.model.common.ResponseModel; import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO; import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -10,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll; import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -24,24 +27,46 @@ import javax.servlet.http.HttpServletRequest;
@Api(tags = "管理后台 - 验证码") @Api(tags = "管理后台 - 验证码")
@RestController("adminCaptchaController") @RestController("adminCaptchaController")
@RequestMapping("/system/captcha") @RequestMapping("/system/captcha")
public class CaptchaController extends com.anji.captcha.controller.CaptchaController { public class CaptchaController {
@Resource
private CaptchaService captchaService;
@PostMapping({"/get"}) @PostMapping({"/get"})
@ApiOperation("获得验证码") @ApiOperation("获得验证码")
@PermitAll @PermitAll
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志 @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
@Override
public ResponseModel get(@RequestBody CaptchaVO data, HttpServletRequest request) { public ResponseModel get(@RequestBody CaptchaVO data, HttpServletRequest request) {
return super.get(data, request); assert request.getRemoteHost() != null;
data.setBrowserInfo(getRemoteId(request));
return captchaService.get(data);
} }
@PostMapping("/check") @PostMapping("/check")
@ApiOperation("校验验证码") @ApiOperation("校验验证码")
@PermitAll @PermitAll
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志 @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
@Override
public ResponseModel check(@RequestBody CaptchaVO data, HttpServletRequest request) { public ResponseModel check(@RequestBody CaptchaVO data, HttpServletRequest request) {
return super.check(data, request); data.setBrowserInfo(getRemoteId(request));
return captchaService.check(data);
}
public static String getRemoteId(HttpServletRequest request) {
String xfwd = request.getHeader("X-Forwarded-For");
String ip = getRemoteIpFromXfwd(xfwd);
String ua = request.getHeader("user-agent");
if (StrUtil.isNotBlank(ip)) {
return ip + ua;
}
return request.getRemoteAddr() + ua;
}
private static String getRemoteIpFromXfwd(String xfwd) {
if (StrUtil.isNotBlank(xfwd)) {
String[] ipList = xfwd.split(",");
return StrUtil.trim(ipList[0]);
}
return null;
} }
} }