feat: 将aj-captcha换为源码导入
@ -0,0 +1,14 @@
|
|||||||
|
package com.anji.captcha.config;
|
||||||
|
|
||||||
|
import com.anji.captcha.properties.AjCaptchaProperties;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties(AjCaptchaProperties.class)
|
||||||
|
@ComponentScan("com.anji.captcha")
|
||||||
|
@Import({AjCaptchaServiceAutoConfiguration.class, AjCaptchaStorageAutoConfiguration.class})
|
||||||
|
public class AjCaptchaAutoConfiguration {
|
||||||
|
}
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
package com.anji.captcha.config;
|
||||||
|
|
||||||
|
import com.anji.captcha.model.common.Const;
|
||||||
|
import com.anji.captcha.properties.AjCaptchaProperties;
|
||||||
|
import com.anji.captcha.service.CaptchaService;
|
||||||
|
import com.anji.captcha.service.impl.CaptchaServiceFactory;
|
||||||
|
import com.anji.captcha.util.ImageUtils;
|
||||||
|
import com.anji.captcha.util.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
|
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class AjCaptchaServiceAutoConfiguration {
|
||||||
|
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(AjCaptchaServiceAutoConfiguration.class);
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public CaptchaService captchaService(AjCaptchaProperties prop) {
|
||||||
|
logger.info("自定义配置项:{}", prop.toString());
|
||||||
|
Properties config = new Properties();
|
||||||
|
config.put(Const.CAPTCHA_CACHETYPE, prop.getCacheType().name());
|
||||||
|
config.put(Const.CAPTCHA_WATER_MARK, prop.getWaterMark());
|
||||||
|
config.put(Const.CAPTCHA_FONT_TYPE, prop.getFontType());
|
||||||
|
config.put(Const.CAPTCHA_TYPE, prop.getType().getCodeValue());
|
||||||
|
config.put(Const.CAPTCHA_INTERFERENCE_OPTIONS, prop.getInterferenceOptions());
|
||||||
|
config.put(Const.ORIGINAL_PATH_JIGSAW, prop.getJigsaw());
|
||||||
|
config.put(Const.ORIGINAL_PATH_PIC_CLICK, prop.getPicClick());
|
||||||
|
config.put(Const.CAPTCHA_SLIP_OFFSET, prop.getSlipOffset());
|
||||||
|
config.put(Const.CAPTCHA_AES_STATUS, String.valueOf(prop.getAesStatus()));
|
||||||
|
config.put(Const.CAPTCHA_WATER_FONT, prop.getWaterFont());
|
||||||
|
config.put(Const.CAPTCHA_CACAHE_MAX_NUMBER, prop.getCacheNumber());
|
||||||
|
config.put(Const.CAPTCHA_TIMING_CLEAR_SECOND, prop.getTimingClear());
|
||||||
|
|
||||||
|
config.put(Const.HISTORY_DATA_CLEAR_ENABLE, prop.isHistoryDataClearEnable() ? "1" : "0");
|
||||||
|
|
||||||
|
config.put(Const.REQ_FREQUENCY_LIMIT_ENABLE, prop.getReqFrequencyLimitEnable() ? "1" : "0");
|
||||||
|
config.put(Const.REQ_GET_LOCK_LIMIT, prop.getReqGetLockLimit() + "");
|
||||||
|
config.put(Const.REQ_GET_LOCK_SECONDS, prop.getReqGetLockSeconds() + "");
|
||||||
|
config.put(Const.REQ_GET_MINUTE_LIMIT, prop.getReqGetMinuteLimit() + "");
|
||||||
|
config.put(Const.REQ_CHECK_MINUTE_LIMIT, prop.getReqCheckMinuteLimit() + "");
|
||||||
|
config.put(Const.REQ_VALIDATE_MINUTE_LIMIT, prop.getReqVerifyMinuteLimit() + "");
|
||||||
|
|
||||||
|
config.put(Const.CAPTCHA_FONT_SIZE, prop.getFontSize() + "");
|
||||||
|
config.put(Const.CAPTCHA_FONT_STYLE, prop.getFontStyle() + "");
|
||||||
|
config.put(Const.CAPTCHA_WORD_COUNT, prop.getClickWordCount() + "");
|
||||||
|
|
||||||
|
if ((StringUtils.isNotBlank(prop.getJigsaw()) && prop.getJigsaw().startsWith("classpath:"))
|
||||||
|
|| (StringUtils.isNotBlank(prop.getPicClick()) && prop.getPicClick().startsWith("classpath:"))) {
|
||||||
|
//自定义resources目录下初始化底图
|
||||||
|
config.put(Const.CAPTCHA_INIT_ORIGINAL, "true");
|
||||||
|
initializeBaseMap(prop.getJigsaw(), prop.getPicClick());
|
||||||
|
}
|
||||||
|
CaptchaService s = CaptchaServiceFactory.getInstance(config);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initializeBaseMap(String jigsaw, String picClick) {
|
||||||
|
ImageUtils.cacheBootImage(getResourcesImagesFile(jigsaw + "/original/*.png"),
|
||||||
|
getResourcesImagesFile(jigsaw + "/slidingBlock/*.png"),
|
||||||
|
getResourcesImagesFile(picClick + "/*.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> getResourcesImagesFile(String path) {
|
||||||
|
Map<String, String> imgMap = new HashMap<>();
|
||||||
|
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||||
|
try {
|
||||||
|
Resource[] resources = resolver.getResources(path);
|
||||||
|
for (Resource resource : resources) {
|
||||||
|
byte[] bytes = FileCopyUtils.copyToByteArray(resource.getInputStream());
|
||||||
|
String string = Base64Utils.encodeToString(bytes);
|
||||||
|
String filename = resource.getFilename();
|
||||||
|
imgMap.put(filename, string);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return imgMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.anji.captcha.config;
|
||||||
|
|
||||||
|
import com.anji.captcha.properties.AjCaptchaProperties;
|
||||||
|
import com.anji.captcha.service.CaptchaCacheService;
|
||||||
|
import com.anji.captcha.service.impl.CaptchaServiceFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储策略自动配置.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class AjCaptchaStorageAutoConfiguration {
|
||||||
|
|
||||||
|
@Bean(name = "AjCaptchaCacheService")
|
||||||
|
public CaptchaCacheService captchaCacheService(AjCaptchaProperties ajCaptchaProperties){
|
||||||
|
//缓存类型redis/local/....
|
||||||
|
return CaptchaServiceFactory.getCache(ajCaptchaProperties.getCacheType().name());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
*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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
package com.anji.captcha.model.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 底图类型枚举
|
||||||
|
*/
|
||||||
|
public enum CaptchaBaseMapEnum {
|
||||||
|
ORIGINAL("ORIGINAL","滑动拼图底图"),
|
||||||
|
SLIDING_BLOCK("SLIDING_BLOCK","滑动拼图滑块底图"),
|
||||||
|
PIC_CLICK("PIC_CLICK","文字点选底图");
|
||||||
|
|
||||||
|
private String codeValue;
|
||||||
|
private 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获取枚举
|
||||||
|
public static CaptchaBaseMapEnum parseFromCodeValue(String codeValue){
|
||||||
|
for (CaptchaBaseMapEnum e : CaptchaBaseMapEnum.values()){
|
||||||
|
if(e.codeValue.equals(codeValue)){ return e;}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据codeValue获取描述
|
||||||
|
public static String getCodeDescByCodeBalue(String codeValue){
|
||||||
|
CaptchaBaseMapEnum enumItem = parseFromCodeValue(codeValue);
|
||||||
|
return enumItem == null ? "" : enumItem.getCodeDesc();
|
||||||
|
}
|
||||||
|
|
||||||
|
//验证codeValue是否有效
|
||||||
|
public static boolean validateCodeValue(String codeValue){ return parseFromCodeValue(codeValue)!=null;}
|
||||||
|
|
||||||
|
//列出所有值字符串
|
||||||
|
public static String getString(){
|
||||||
|
StringBuffer buffer = new StringBuffer();
|
||||||
|
for (CaptchaBaseMapEnum e : CaptchaBaseMapEnum.values()){
|
||||||
|
buffer.append(e.codeValue).append("--").append(e.getCodeDesc()).append(", ");
|
||||||
|
}
|
||||||
|
buffer.deleteCharAt(buffer.lastIndexOf(","));
|
||||||
|
return buffer.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
package com.anji.captcha.model.common;
|
||||||
|
|
||||||
|
public enum CaptchaTypeEnum {
|
||||||
|
/**
|
||||||
|
* 滑块拼图.
|
||||||
|
*/
|
||||||
|
BLOCKPUZZLE("blockPuzzle","滑块拼图"),
|
||||||
|
/**
|
||||||
|
* 文字点选.
|
||||||
|
*/
|
||||||
|
CLICKWORD("clickWord","文字点选"),
|
||||||
|
/**
|
||||||
|
* 默认.
|
||||||
|
*/
|
||||||
|
DEFAULT("default","默认");
|
||||||
|
|
||||||
|
private String codeValue;
|
||||||
|
private 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获取枚举
|
||||||
|
public static CaptchaTypeEnum parseFromCodeValue(String codeValue){
|
||||||
|
for (CaptchaTypeEnum e : CaptchaTypeEnum.values()){
|
||||||
|
if(e.codeValue.equals(codeValue)){ return e;}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据codeValue获取描述
|
||||||
|
public static String getCodeDescByCodeBalue(String codeValue){
|
||||||
|
CaptchaTypeEnum enumItem = parseFromCodeValue(codeValue);
|
||||||
|
return enumItem == null ? "" : enumItem.getCodeDesc();
|
||||||
|
}
|
||||||
|
|
||||||
|
//验证codeValue是否有效
|
||||||
|
public static boolean validateCodeValue(String codeValue){ return parseFromCodeValue(codeValue)!=null;}
|
||||||
|
|
||||||
|
//列出所有值字符串
|
||||||
|
public static String getString(){
|
||||||
|
StringBuffer buffer = new StringBuffer();
|
||||||
|
for (CaptchaTypeEnum e : CaptchaTypeEnum.values()){
|
||||||
|
buffer.append(e.codeValue).append("--").append(e.getCodeDesc()).append(", ");
|
||||||
|
}
|
||||||
|
buffer.deleteCharAt(buffer.lastIndexOf(","));
|
||||||
|
return buffer.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
*Copyright © 2018 anji-plus
|
||||||
|
*安吉加加信息技术有限公司
|
||||||
|
*http://www.anji-plus.com
|
||||||
|
*All rights reserved.
|
||||||
|
*/
|
||||||
|
package com.anji.captcha.model.common;
|
||||||
|
|
||||||
|
import com.anji.captcha.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RequestModel implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -5800786065305114784L;
|
||||||
|
|
||||||
|
/**当前请求接口路径 /business/accessUser/login */
|
||||||
|
private String servletPath;
|
||||||
|
|
||||||
|
/** {"reqData":{"password":"*****","userName":"admin"},"sign":"a304a7f296f565b6d2009797f68180f0","time":"1542456453355","token":""} */
|
||||||
|
private String requestString;
|
||||||
|
|
||||||
|
/** {"password":"****","userName":"admin"} */
|
||||||
|
private HashMap reqData;
|
||||||
|
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
private List<Long> projectList;
|
||||||
|
|
||||||
|
//拥有哪些分组
|
||||||
|
private List<Long> groupIdList;
|
||||||
|
|
||||||
|
private String target;
|
||||||
|
|
||||||
|
private String sign;
|
||||||
|
|
||||||
|
private String time;
|
||||||
|
|
||||||
|
private String sourceIP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验自身参数合法性
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isVaildateRequest() {
|
||||||
|
if (StringUtils.isBlank(sign) || StringUtils.isBlank(time)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServletPath() {
|
||||||
|
return servletPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServletPath(String 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
*Copyright © 2018 anji-plus
|
||||||
|
*安吉加加信息技术有限公司
|
||||||
|
*http://www.anji-plus.com
|
||||||
|
*All rights reserved.
|
||||||
|
*/
|
||||||
|
package com.anji.captcha.model.common;
|
||||||
|
|
||||||
|
import com.anji.captcha.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class ResponseModel implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 8445617032523881407L;
|
||||||
|
|
||||||
|
private String repCode;
|
||||||
|
|
||||||
|
private String repMsg;
|
||||||
|
|
||||||
|
private Object repData;
|
||||||
|
|
||||||
|
public ResponseModel() {
|
||||||
|
this.repCode = RepCodeEnum.SUCCESS.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResponseModel(RepCodeEnum repCodeEnum) {
|
||||||
|
this.setRepCodeEnum(repCodeEnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
//成功
|
||||||
|
public static ResponseModel success(){
|
||||||
|
return ResponseModel.successMsg("成功");
|
||||||
|
}
|
||||||
|
public static ResponseModel successMsg(String message){
|
||||||
|
ResponseModel responseModel = new ResponseModel();
|
||||||
|
responseModel.setRepMsg(message);
|
||||||
|
return responseModel;
|
||||||
|
}
|
||||||
|
public static ResponseModel successData(Object data){
|
||||||
|
ResponseModel responseModel = new ResponseModel();
|
||||||
|
responseModel.setRepCode(RepCodeEnum.SUCCESS.getCode());
|
||||||
|
responseModel.setRepData(data);
|
||||||
|
return responseModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
//失败
|
||||||
|
public static ResponseModel errorMsg(RepCodeEnum message){
|
||||||
|
ResponseModel responseModel = new ResponseModel();
|
||||||
|
responseModel.setRepCodeEnum(message);
|
||||||
|
return responseModel;
|
||||||
|
}
|
||||||
|
public static ResponseModel errorMsg(String message){
|
||||||
|
ResponseModel responseModel = new ResponseModel();
|
||||||
|
responseModel.setRepCode(RepCodeEnum.ERROR.getCode());
|
||||||
|
responseModel.setRepMsg(message);
|
||||||
|
return responseModel;
|
||||||
|
}
|
||||||
|
public static ResponseModel errorMsg(RepCodeEnum repCodeEnum, String message){
|
||||||
|
ResponseModel responseModel = new ResponseModel();
|
||||||
|
responseModel.setRepCode(repCodeEnum.getCode());
|
||||||
|
responseModel.setRepMsg(message);
|
||||||
|
return responseModel;
|
||||||
|
}
|
||||||
|
public static ResponseModel exceptionMsg(String message){
|
||||||
|
ResponseModel responseModel = new ResponseModel();
|
||||||
|
responseModel.setRepCode(RepCodeEnum.EXCEPTION.getCode());
|
||||||
|
responseModel.setRepMsg(RepCodeEnum.EXCEPTION.getDesc() + ": " + message);
|
||||||
|
return responseModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ResponseModel{" + "repCode='" + repCode + '\'' + ", repMsg='"
|
||||||
|
+ repMsg + '\'' + ", repData=" + repData + '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuccess(){
|
||||||
|
return StringUtils.equals(repCode, RepCodeEnum.SUCCESS.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRepCode() {
|
||||||
|
return repCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRepCode(String repCode) {
|
||||||
|
this.repCode = repCode;
|
||||||
|
}
|
||||||
|
public void setRepCodeEnum(RepCodeEnum repCodeEnum) {
|
||||||
|
this.repCode=repCodeEnum.getCode();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
package com.anji.captcha.model.vo;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by raodeming on 2020/5/16.
|
||||||
|
*/
|
||||||
|
public class PointVO {
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
public int x;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
this.secretKey = secretKey;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PointVO() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PointVO(int x, int y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toJsonString() {
|
||||||
|
return String.format("{\"secretKey\":\"%s\",\"x\":%d,\"y\":%d}", secretKey, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PointVO parse(String jsonStr) {
|
||||||
|
Map<String, Object> m = new HashMap();
|
||||||
|
Arrays.stream(jsonStr
|
||||||
|
.replaceFirst(",\\{", "\\{")
|
||||||
|
.replaceFirst("\\{", "")
|
||||||
|
.replaceFirst("\\}", "")
|
||||||
|
.replaceAll("\"", "")
|
||||||
|
.split(",")).forEach(item -> {
|
||||||
|
m.put(item.split(":")[0], item.split(":")[1]);
|
||||||
|
});
|
||||||
|
//PointVO d = new PointVO();
|
||||||
|
setX(Double.valueOf("" + m.get("x")).intValue());
|
||||||
|
setY(Double.valueOf("" + m.get("y")).intValue());
|
||||||
|
setSecretKey(m.getOrDefault("secretKey", "") + "");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PointVO pointVO = (PointVO) o;
|
||||||
|
return x == pointVO.x && y == pointVO.y && Objects.equals(secretKey, pointVO.secretKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
|
||||||
|
return Objects.hash(secretKey, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
*Copyright © 2018 anji-plus
|
||||||
|
*安吉加加信息技术有限公司
|
||||||
|
*http://www.anji-plus.com
|
||||||
|
*All rights reserved.
|
||||||
|
*/
|
||||||
|
package com.anji.captcha.service.impl;
|
||||||
|
|
||||||
|
import com.anji.captcha.model.common.RepCodeEnum;
|
||||||
|
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 java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by raodeming on 2019/12/25.
|
||||||
|
*/
|
||||||
|
public class DefaultCaptchaServiceImpl extends AbstractCaptchaService{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String captchaType() {
|
||||||
|
return "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Properties config) {
|
||||||
|
for (String s : CaptchaServiceFactory.instances.keySet()) {
|
||||||
|
if(captchaType().equals(s)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
getService(s).init(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy(Properties config) {
|
||||||
|
for (String s : CaptchaServiceFactory.instances.keySet()) {
|
||||||
|
if(captchaType().equals(s)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
getService(s).destroy(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CaptchaService getService(String captchaType){
|
||||||
|
return CaptchaServiceFactory.instances.get(captchaType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseModel get(CaptchaVO captchaVO) {
|
||||||
|
if (captchaVO == null) {
|
||||||
|
return RepCodeEnum.NULL_ERROR.parseError("captchaVO");
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(captchaVO.getCaptchaType())) {
|
||||||
|
return RepCodeEnum.NULL_ERROR.parseError("类型");
|
||||||
|
}
|
||||||
|
return getService(captchaVO.getCaptchaType()).get(captchaVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseModel check(CaptchaVO captchaVO) {
|
||||||
|
if (captchaVO == null) {
|
||||||
|
return RepCodeEnum.NULL_ERROR.parseError("captchaVO");
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(captchaVO.getCaptchaType())) {
|
||||||
|
return RepCodeEnum.NULL_ERROR.parseError("类型");
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(captchaVO.getToken())) {
|
||||||
|
return RepCodeEnum.NULL_ERROR.parseError("token");
|
||||||
|
}
|
||||||
|
return getService(captchaVO.getCaptchaType()).check(captchaVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseModel verification(CaptchaVO captchaVO) {
|
||||||
|
if (captchaVO == null) {
|
||||||
|
return RepCodeEnum.NULL_ERROR.parseError("captchaVO");
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(captchaVO.getCaptchaVerification())) {
|
||||||
|
return RepCodeEnum.NULL_ERROR.parseError("二次校验参数");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String codeKey = String.format(REDIS_SECOND_CAPTCHA_KEY, captchaVO.getCaptchaVerification());
|
||||||
|
if (!CaptchaServiceFactory.getCache(cacheType).exists(codeKey)) {
|
||||||
|
return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_INVALID);
|
||||||
|
}
|
||||||
|
//二次校验取值后,即刻失效
|
||||||
|
CaptchaServiceFactory.getCache(cacheType).delete(codeKey);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("验证码坐标解析失败", e);
|
||||||
|
return ResponseModel.errorMsg(e.getMessage());
|
||||||
|
}
|
||||||
|
return ResponseModel.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,121 @@
|
|||||||
|
package com.anji.captcha.util;
|
||||||
|
//
|
||||||
|
// Source code recreated from a .class file by IntelliJ IDEA
|
||||||
|
// (powered by Fernflower decompiler)
|
||||||
|
//
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
|
||||||
|
public abstract class FileCopyUtils {
|
||||||
|
public static final int BUFFER_SIZE = 4096;
|
||||||
|
|
||||||
|
public FileCopyUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int copy(File in, File out) throws IOException {
|
||||||
|
return copy(Files.newInputStream(in.toPath()), Files.newOutputStream(out.toPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copy(byte[] in, File out) throws IOException {
|
||||||
|
copy((InputStream)(new ByteArrayInputStream(in)), (OutputStream)Files.newOutputStream(out.toPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] copyToByteArray(File in) throws IOException {
|
||||||
|
return copyToByteArray(Files.newInputStream(in.toPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int copy(InputStream in, OutputStream out) throws IOException {
|
||||||
|
int var2;
|
||||||
|
try {
|
||||||
|
var2 = StreamUtils.copy(in, out);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (IOException var12) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
out.close();
|
||||||
|
} catch (IOException var11) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copy(byte[] in, OutputStream out) throws IOException {
|
||||||
|
try {
|
||||||
|
out.write(in);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
out.close();
|
||||||
|
} catch (IOException var8) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] copyToByteArray(InputStream in) throws IOException {
|
||||||
|
if (in == null) {
|
||||||
|
return new byte[0];
|
||||||
|
} else {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
|
||||||
|
copy((InputStream)in, (OutputStream)out);
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int copy(Reader in, Writer out) throws IOException {
|
||||||
|
try {
|
||||||
|
int byteCount = 0;
|
||||||
|
char[] buffer = new char[4096];
|
||||||
|
|
||||||
|
int bytesRead;
|
||||||
|
for(boolean var4 = true; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.flush();
|
||||||
|
int var5 = byteCount;
|
||||||
|
return var5;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (IOException var15) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
out.close();
|
||||||
|
} catch (IOException var14) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copy(String in, Writer out) throws IOException {
|
||||||
|
try {
|
||||||
|
out.write(in);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
out.close();
|
||||||
|
} catch (IOException var8) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String copyToString(Reader in) throws IOException {
|
||||||
|
if (in == null) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
StringWriter out = new StringWriter();
|
||||||
|
copy((Reader)in, (Writer)out);
|
||||||
|
return out.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
*Copyright © 2018 anji-plus
|
||||||
|
*安吉加加信息技术有限公司
|
||||||
|
*http://www.anji-plus.com
|
||||||
|
*All rights reserved.
|
||||||
|
*/
|
||||||
|
package com.anji.captcha.util;
|
||||||
|
|
||||||
|
import com.anji.captcha.model.common.CaptchaBaseMapEnum;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
|
||||||
|
public class ImageUtils {
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(ImageUtils.class);
|
||||||
|
private static Map<String, String> originalCacheMap = new ConcurrentHashMap(); //滑块底图
|
||||||
|
private static Map<String, String> slidingBlockCacheMap = new ConcurrentHashMap(); //滑块
|
||||||
|
private static Map<String, String> picClickCacheMap = new ConcurrentHashMap(); //点选文字
|
||||||
|
private static Map<String, String[]> fileNameMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static void cacheImage(String captchaOriginalPathJigsaw, String captchaOriginalPathClick) {
|
||||||
|
//滑动拼图
|
||||||
|
if (StringUtils.isBlank(captchaOriginalPathJigsaw)) {
|
||||||
|
originalCacheMap.putAll(getResourcesImagesFile("defaultImages/jigsaw/original"));
|
||||||
|
slidingBlockCacheMap.putAll(getResourcesImagesFile("defaultImages/jigsaw/slidingBlock"));
|
||||||
|
} else {
|
||||||
|
originalCacheMap.putAll(getImagesFile(captchaOriginalPathJigsaw + File.separator + "original"));
|
||||||
|
slidingBlockCacheMap.putAll(getImagesFile(captchaOriginalPathJigsaw + File.separator + "slidingBlock"));
|
||||||
|
}
|
||||||
|
//点选文字
|
||||||
|
if (StringUtils.isBlank(captchaOriginalPathClick)) {
|
||||||
|
picClickCacheMap.putAll(getResourcesImagesFile("defaultImages/pic-click"));
|
||||||
|
} else {
|
||||||
|
picClickCacheMap.putAll(getImagesFile(captchaOriginalPathClick));
|
||||||
|
}
|
||||||
|
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.PIC_CLICK.getCodeValue(), picClickCacheMap.keySet().toArray(new String[0]));
|
||||||
|
logger.info("初始化底图:{}", JsonUtil.toJSONString(fileNameMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void cacheBootImage(Map<String, String> originalMap, Map<String, String> slidingBlockMap, Map<String, String> picClickMap) {
|
||||||
|
originalCacheMap.putAll(originalMap);
|
||||||
|
slidingBlockCacheMap.putAll(slidingBlockMap);
|
||||||
|
picClickCacheMap.putAll(picClickMap);
|
||||||
|
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.PIC_CLICK.getCodeValue(), picClickCacheMap.keySet().toArray(new String[0]));
|
||||||
|
logger.info("自定义resource底图:{}", JsonUtil.toJSONString(fileNameMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static BufferedImage getOriginal() {
|
||||||
|
String[] strings = fileNameMap.get(CaptchaBaseMapEnum.ORIGINAL.getCodeValue());
|
||||||
|
if (null == strings || strings.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Integer randomInt = RandomUtils.getRandomInt(0, strings.length);
|
||||||
|
String s = originalCacheMap.get(strings[randomInt]);
|
||||||
|
return getBase64StrToImage(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getslidingBlock() {
|
||||||
|
String[] strings = fileNameMap.get(CaptchaBaseMapEnum.SLIDING_BLOCK.getCodeValue());
|
||||||
|
if (null == strings || strings.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Integer randomInt = RandomUtils.getRandomInt(0, strings.length);
|
||||||
|
String s = slidingBlockCacheMap.get(strings[randomInt]);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BufferedImage getPicClick() {
|
||||||
|
String[] strings = fileNameMap.get(CaptchaBaseMapEnum.PIC_CLICK.getCodeValue());
|
||||||
|
if (null == strings || strings.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Integer randomInt = RandomUtils.getRandomInt(0, strings.length);
|
||||||
|
String s = picClickCacheMap.get(strings[randomInt]);
|
||||||
|
return getBase64StrToImage(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片转base64 字符串
|
||||||
|
*
|
||||||
|
* @param templateImage
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getImageToBase64Str(BufferedImage templateImage) {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
ImageIO.write(templateImage, "png", baos);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
byte[] bytes = baos.toByteArray();
|
||||||
|
|
||||||
|
Base64.Encoder encoder = Base64.getEncoder();
|
||||||
|
|
||||||
|
return encoder.encodeToString(bytes).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* base64 字符串转图片
|
||||||
|
*
|
||||||
|
* @param base64String
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static BufferedImage getBase64StrToImage(String base64String) {
|
||||||
|
try {
|
||||||
|
Base64.Decoder decoder = Base64.getDecoder();
|
||||||
|
byte[] bytes = decoder.decode(base64String);
|
||||||
|
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
|
||||||
|
return ImageIO.read(inputStream);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Map<String, String> getResourcesImagesFile(String path) {
|
||||||
|
//默认提供六张底图
|
||||||
|
Map<String, String> imgMap = new HashMap<>();
|
||||||
|
ClassLoader classLoader = ImageUtils.class.getClassLoader();
|
||||||
|
for (int i = 1; i <= 6; i++) {
|
||||||
|
InputStream resourceAsStream = classLoader.getResourceAsStream(path.concat("/").concat(String.valueOf(i).concat(".png")));
|
||||||
|
byte[] bytes = new byte[0];
|
||||||
|
try {
|
||||||
|
bytes = FileCopyUtils.copyToByteArray(resourceAsStream);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
String string = Base64Utils.encodeToString(bytes);
|
||||||
|
String filename = String.valueOf(i).concat(".png");
|
||||||
|
imgMap.put(filename, string);
|
||||||
|
}
|
||||||
|
return imgMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> getImagesFile(String path) {
|
||||||
|
Map<String, String> imgMap = new HashMap<>();
|
||||||
|
File file = new File(path);
|
||||||
|
if (!file.exists()) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
File[] files = file.listFiles();
|
||||||
|
Arrays.stream(files).forEach(item -> {
|
||||||
|
try {
|
||||||
|
FileInputStream fileInputStream = new FileInputStream(item);
|
||||||
|
byte[] bytes = FileCopyUtils.copyToByteArray(fileInputStream);
|
||||||
|
String string = Base64Utils.encodeToString(bytes);
|
||||||
|
imgMap.put(item.getName(), string);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return imgMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
package com.anji.captcha.util;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Title: MD5工具类
|
||||||
|
*/
|
||||||
|
public abstract class MD5Util {
|
||||||
|
/**
|
||||||
|
* 获取指定字符串的md5值
|
||||||
|
* @param dataStr 明文
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public static String md5(String dataStr) {
|
||||||
|
try {
|
||||||
|
MessageDigest m = MessageDigest.getInstance("MD5");
|
||||||
|
m.update(dataStr.getBytes("UTF8"));
|
||||||
|
byte[] s = m.digest();
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
for (int i = 0; i < s.length; i++) {
|
||||||
|
result.append(Integer.toHexString((0x000000FF & s[i]) | 0xFFFFFF00).substring(6));
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定字符串的md5值, md5(str+salt)
|
||||||
|
* @param dataStr 明文
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public static String md5WithSalt(String dataStr,String salt) {
|
||||||
|
return md5(dataStr + salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
*Copyright © 2018 anji-plus
|
||||||
|
*安吉加加信息技术有限公司
|
||||||
|
*http://www.anji-plus.com
|
||||||
|
*All rights reserved.
|
||||||
|
*/
|
||||||
|
package com.anji.captcha.util;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
|
||||||
|
public class RandomUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成UUID
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getUUID() {
|
||||||
|
String uuid = UUID.randomUUID().toString();
|
||||||
|
uuid = uuid.replace("-", "");
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定文字的随机中文
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getRandomHan(String hanZi) {
|
||||||
|
String ch = hanZi.charAt(new Random().nextInt(hanZi.length())) + "";
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getRandomInt(int bound){
|
||||||
|
return ThreadLocalRandom.current().nextInt(bound);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取随机中文
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getRandomHan() {
|
||||||
|
String str = "";
|
||||||
|
int highCode;
|
||||||
|
int lowCode;
|
||||||
|
|
||||||
|
Random random = new Random();
|
||||||
|
|
||||||
|
highCode = (176 + Math.abs(random.nextInt(39))); //B0 + 0~39(16~55) 一级汉字所占区
|
||||||
|
lowCode = (161 + Math.abs(random.nextInt(93))); //A1 + 0~93 每区有94个汉字
|
||||||
|
|
||||||
|
byte[] b = new byte[2];
|
||||||
|
b[0] = (Integer.valueOf(highCode)).byteValue();
|
||||||
|
b[1] = (Integer.valueOf(lowCode)).byteValue();
|
||||||
|
|
||||||
|
try {
|
||||||
|
str = new String(b, "GBK");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机范围内数字
|
||||||
|
* @param startNum
|
||||||
|
* @param endNum
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Integer getRandomInt(int startNum, int endNum) {
|
||||||
|
return ThreadLocalRandom.current().nextInt(endNum-startNum) + startNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取随机字符串
|
||||||
|
* @param length
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getRandomString(int length){
|
||||||
|
String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
|
Random random=new Random();
|
||||||
|
StringBuffer sb=new StringBuffer();
|
||||||
|
for(int i=0;i<length;i++){
|
||||||
|
int number=random.nextInt(62);
|
||||||
|
sb.append(str.charAt(number));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,139 @@
|
|||||||
|
package com.anji.captcha.util;
|
||||||
|
//
|
||||||
|
// Source code recreated from a .class file by IntelliJ IDEA
|
||||||
|
// (powered by Fernflower decompiler)
|
||||||
|
//
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
public abstract class StreamUtils {
|
||||||
|
public static final int BUFFER_SIZE = 4096;
|
||||||
|
private static final byte[] EMPTY_CONTENT = new byte[0];
|
||||||
|
|
||||||
|
public StreamUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] copyToByteArray(InputStream in) throws IOException {
|
||||||
|
if (in == null) {
|
||||||
|
return new byte[0];
|
||||||
|
} else {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
|
||||||
|
copy((InputStream)in, out);
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String copyToString(InputStream in, Charset charset) throws IOException {
|
||||||
|
if (in == null) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
StringBuilder out = new StringBuilder();
|
||||||
|
InputStreamReader reader = new InputStreamReader(in, charset);
|
||||||
|
char[] buffer = new char[4096];
|
||||||
|
boolean var5 = true;
|
||||||
|
|
||||||
|
int bytesRead;
|
||||||
|
while((bytesRead = reader.read(buffer)) != -1) {
|
||||||
|
out.append(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copy(byte[] in, OutputStream out) throws IOException {
|
||||||
|
out.write(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copy(String in, Charset charset, OutputStream out) throws IOException {
|
||||||
|
Writer writer = new OutputStreamWriter(out, charset);
|
||||||
|
writer.write(in);
|
||||||
|
writer.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int copy(InputStream in, OutputStream out) throws IOException {
|
||||||
|
int byteCount = 0;
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
|
||||||
|
int bytesRead;
|
||||||
|
for(boolean var4 = true; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.flush();
|
||||||
|
return byteCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long copyRange(InputStream in, OutputStream out, long start, long end) throws IOException {
|
||||||
|
long skipped = in.skip(start);
|
||||||
|
if (skipped < start) {
|
||||||
|
throw new IOException("Skipped only " + skipped + " bytes out of " + start + " required");
|
||||||
|
} else {
|
||||||
|
long bytesToCopy = end - start + 1L;
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
|
||||||
|
while(bytesToCopy > 0L) {
|
||||||
|
int bytesRead = in.read(buffer);
|
||||||
|
if (bytesRead == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((long)bytesRead <= bytesToCopy) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
bytesToCopy -= (long)bytesRead;
|
||||||
|
} else {
|
||||||
|
out.write(buffer, 0, (int)bytesToCopy);
|
||||||
|
bytesToCopy = 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return end - start + 1L - bytesToCopy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int drain(InputStream in) throws IOException {
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int byteCount;
|
||||||
|
int bytesRead;
|
||||||
|
for(byteCount = 0; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return byteCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InputStream emptyInput() {
|
||||||
|
return new ByteArrayInputStream(EMPTY_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InputStream nonClosing(InputStream in) {
|
||||||
|
return new NonClosingInputStream(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OutputStream nonClosing(OutputStream out) {
|
||||||
|
return new NonClosingOutputStream(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NonClosingOutputStream extends FilterOutputStream {
|
||||||
|
public NonClosingOutputStream(OutputStream out) {
|
||||||
|
super(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(byte[] b, int off, int let) throws IOException {
|
||||||
|
this.out.write(b, off, let);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NonClosingInputStream extends FilterInputStream {
|
||||||
|
public NonClosingInputStream(InputStream in) {
|
||||||
|
super(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
com.anji.captcha.service.impl.BlockPuzzleCaptchaServiceImpl
|
||||||
|
com.anji.captcha.service.impl.ClickWordCaptchaServiceImpl
|
||||||
|
com.anji.captcha.service.impl.DefaultCaptchaServiceImpl
|
||||||
@ -1 +1,2 @@
|
|||||||
|
com.anji.captcha.config.AjCaptchaAutoConfiguration
|
||||||
cn.iocoder.yudao.framework.captcha.config.YudaoCaptchaConfiguration
|
cn.iocoder.yudao.framework.captcha.config.YudaoCaptchaConfiguration
|
||||||
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 58 KiB |