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
|
||||
|
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 |