Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/wechat-mp
# Conflicts: # yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/DefaultDatabaseQueryTest.javaplp
@ -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,89 @@
|
||||
package com.anji.captcha.config;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
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;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class AjCaptchaServiceAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CaptchaService captchaService(AjCaptchaProperties prop) {
|
||||
log.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 ((StrUtil.isNotBlank(prop.getJigsaw()) && prop.getJigsaw().startsWith("classpath:"))
|
||||
|| (StrUtil.isNotBlank(prop.getPicClick()) && prop.getPicClick().startsWith("classpath:"))) {
|
||||
//自定义resources目录下初始化底图
|
||||
config.put(Const.CAPTCHA_INIT_ORIGINAL, "true");
|
||||
initializeBaseMap(prop.getJigsaw(), prop.getPicClick());
|
||||
}
|
||||
return CaptchaServiceFactory.getInstance(config);
|
||||
}
|
||||
|
||||
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,20 @@
|
||||
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,50 @@
|
||||
package com.anji.captcha.model.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 底图类型枚举
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum CaptchaBaseMapEnum {
|
||||
ORIGINAL("ORIGINAL", "滑动拼图底图"),
|
||||
SLIDING_BLOCK("SLIDING_BLOCK", "滑动拼图滑块底图"),
|
||||
PIC_CLICK("PIC_CLICK", "文字点选底图");
|
||||
|
||||
private final String codeValue;
|
||||
private final String 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;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum CaptchaTypeEnum {
|
||||
/**
|
||||
* 滑块拼图.
|
||||
*/
|
||||
BLOCKPUZZLE("blockPuzzle", "滑块拼图"),
|
||||
/**
|
||||
* 文字点选.
|
||||
*/
|
||||
CLICKWORD("clickWord", "文字点选"),
|
||||
/**
|
||||
* 默认.
|
||||
*/
|
||||
DEFAULT("default", "默认");
|
||||
|
||||
private final String codeValue;
|
||||
private final String 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() {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
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,76 @@
|
||||
/*
|
||||
*Copyright © 2018 anji-plus
|
||||
*安吉加加信息技术有限公司
|
||||
*http://www.anji-plus.com
|
||||
*All rights reserved.
|
||||
*/
|
||||
package com.anji.captcha.model.common;
|
||||
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
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 (StrUtil.isBlank(sign) || StrUtil.isBlank(time)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getServletPath() {
|
||||
return servletPath;
|
||||
}
|
||||
|
||||
public void setServletPath(String servletPath) {
|
||||
this.servletPath = servletPath;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
/*
|
||||
*Copyright © 2018 anji-plus
|
||||
*安吉加加信息技术有限公司
|
||||
*http://www.anji-plus.com
|
||||
*All rights reserved.
|
||||
*/
|
||||
package com.anji.captcha.model.common;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
public boolean isSuccess() {
|
||||
return StrUtil.equals(repCode, RepCodeEnum.SUCCESS.getCode());
|
||||
}
|
||||
|
||||
public String getRepCode() {
|
||||
return repCode;
|
||||
}
|
||||
|
||||
public void setRepCodeEnum(RepCodeEnum repCodeEnum) {
|
||||
this.repCode = repCodeEnum.getCode();
|
||||
this.repMsg = repCodeEnum.getDesc();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package com.anji.captcha.model.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
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.
|
||||
*/
|
||||
@Data
|
||||
public class PointVO {
|
||||
private String secretKey;
|
||||
|
||||
public int x;
|
||||
|
||||
public int 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,100 @@
|
||||
/*
|
||||
*Copyright © 2018 anji-plus
|
||||
*安吉加加信息技术有限公司
|
||||
*http://www.anji-plus.com
|
||||
*All rights reserved.
|
||||
*/
|
||||
package com.anji.captcha.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.anji.captcha.model.common.RepCodeEnum;
|
||||
import com.anji.captcha.model.common.ResponseModel;
|
||||
import com.anji.captcha.model.vo.CaptchaVO;
|
||||
import com.anji.captcha.service.CaptchaService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Created by raodeming on 2019/12/25.
|
||||
*/
|
||||
@Slf4j
|
||||
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 (StrUtil.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 (StrUtil.isEmpty(captchaVO.getCaptchaType())) {
|
||||
return RepCodeEnum.NULL_ERROR.parseError("类型");
|
||||
}
|
||||
if (StrUtil.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 (StrUtil.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) {
|
||||
log.error("验证码坐标解析失败", e);
|
||||
return ResponseModel.errorMsg(e.getMessage());
|
||||
}
|
||||
return ResponseModel.success();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
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();
|
||||
return byteCount;
|
||||
} 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,169 @@
|
||||
/*
|
||||
*Copyright © 2018 anji-plus
|
||||
*安吉加加信息技术有限公司
|
||||
*http://www.anji-plus.com
|
||||
*All rights reserved.
|
||||
*/
|
||||
package com.anji.captcha.util;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.anji.captcha.model.common.CaptchaBaseMapEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
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;
|
||||
|
||||
@Slf4j
|
||||
public class ImageUtils {
|
||||
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 (StrUtil.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 (StrUtil.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]));
|
||||
log.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]));
|
||||
log.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);
|
||||
return slidingBlockCacheMap.get(strings[randomInt]);
|
||||
}
|
||||
|
||||
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 (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
return imgMap;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package com.anji.captcha.util;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
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(StandardCharsets.UTF_8));
|
||||
byte[] s = m.digest();
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (byte b : s) {
|
||||
result.append(Integer.toHexString((0x000000FF & b) | 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,96 @@
|
||||
/*
|
||||
*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) {
|
||||
return hanZi.charAt(new Random().nextInt(hanZi.length())) + "";
|
||||
}
|
||||
|
||||
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,138 @@
|
||||
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];
|
||||
|
||||
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 |
@ -0,0 +1,15 @@
|
||||
package cn.iocoder.yudao.framework.web.core.clean;
|
||||
|
||||
/**
|
||||
* 对 html 文本中的有 Xss 风险的数据进行清理
|
||||
*/
|
||||
public interface XssCleaner {
|
||||
|
||||
/**
|
||||
* 清理有 Xss 风险的文本
|
||||
*
|
||||
* @param html 原 html
|
||||
* @return 清理后的 html
|
||||
*/
|
||||
String clean(String html);
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
import router from './router'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import { isRelogin } from '@/config/axios/service'
|
||||
import { getAccessToken } from '@/utils/auth'
|
||||
import { useTitle } from '@/hooks/web/useTitle'
|
||||
import { useNProgress } from '@/hooks/web/useNProgress'
|
||||
import { usePageLoading } from '@/hooks/web/usePageLoading'
|
||||
import { useDictStoreWithOut } from '@/store/modules/dict'
|
||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||
import { usePermissionStoreWithOut } from '@/store/modules/permission'
|
||||
|
||||
const { start, done } = useNProgress()
|
||||
|
||||
const { loadStart, loadDone } = usePageLoading()
|
||||
// 路由不重定向白名单
|
||||
const whiteList = [
|
||||
'/login',
|
||||
'/social-login',
|
||||
'/auth-redirect',
|
||||
'/bind',
|
||||
'/register',
|
||||
'/oauthLogin/gitee'
|
||||
]
|
||||
|
||||
// 路由加载前
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
start()
|
||||
loadStart()
|
||||
if (getAccessToken()) {
|
||||
if (to.path === '/login') {
|
||||
next({ path: '/' })
|
||||
} else {
|
||||
// 获取所有字典
|
||||
const dictStore = useDictStoreWithOut()
|
||||
const userStore = useUserStoreWithOut()
|
||||
const permissionStore = usePermissionStoreWithOut()
|
||||
if (!dictStore.getIsSetDict) {
|
||||
dictStore.setDictMap()
|
||||
}
|
||||
if (!userStore.getIsSetUser) {
|
||||
isRelogin.show = true
|
||||
await userStore.setUserInfoAction()
|
||||
isRelogin.show = false
|
||||
// 后端过滤菜单
|
||||
await permissionStore.generateRoutes()
|
||||
permissionStore.getAddRouters.forEach((route) => {
|
||||
router.addRoute(route as unknown as RouteRecordRaw) // 动态添加可访问路由表
|
||||
})
|
||||
const redirectPath = from.query.redirect || to.path
|
||||
const redirect = decodeURIComponent(redirectPath as string)
|
||||
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect }
|
||||
next(nextData)
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
next()
|
||||
} else {
|
||||
next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
router.afterEach((to) => {
|
||||
useTitle(to?.meta?.title as string)
|
||||
done() // 结束Progress
|
||||
loadDone()
|
||||
})
|
||||
@ -1,6 +0,0 @@
|
||||
@import 'vxe-table/styles/variable.scss';
|
||||
@import 'vxe-table/styles/modules.scss';
|
||||
// @import './theme/light.scss';
|
||||
i {
|
||||
border-color: initial;
|
||||
}
|
||||
@ -1,81 +0,0 @@
|
||||
// 修改样式变量
|
||||
//@import 'vxe-table/styles/variable.scss';
|
||||
|
||||
/*font*/
|
||||
$vxe-font-color: #e5e7eb;
|
||||
// $vxe-font-size: 14px !default;
|
||||
// $vxe-font-size-medium: 16px !default;
|
||||
// $vxe-font-size-small: 14px !default;
|
||||
// $vxe-font-size-mini: 12px !default;
|
||||
|
||||
/*color*/
|
||||
$vxe-primary-color: #409eff !default;
|
||||
$vxe-success-color: #67c23a !default;
|
||||
$vxe-info-color: #909399 !default;
|
||||
$vxe-warning-color: #e6a23c !default;
|
||||
$vxe-danger-color: #f56c6c !default;
|
||||
$vxe-disabled-color: #bfbfbf !default;
|
||||
$vxe-primary-disabled-color: #c0c4cc !default;
|
||||
|
||||
/*loading*/
|
||||
$vxe-loading-color: $vxe-primary-color !default;
|
||||
$vxe-loading-background-color: #1d1e1f !default;
|
||||
$vxe-loading-z-index: 999 !default;
|
||||
|
||||
/*icon*/
|
||||
$vxe-icon-font-family: Verdana, Arial, Tahoma !default;
|
||||
$vxe-icon-background-color: #e5e7eb !default;
|
||||
|
||||
/*toolbar*/
|
||||
$vxe-toolbar-background-color: #1d1e1f !default;
|
||||
$vxe-toolbar-button-border: #dcdfe6 !default;
|
||||
$vxe-toolbar-custom-active-background-color: #d9dadb !default;
|
||||
$vxe-toolbar-panel-background-color: #e5e7eb !default;
|
||||
|
||||
$vxe-table-font-color: #e5e7eb;
|
||||
$vxe-table-header-background-color: #1d1e1f;
|
||||
$vxe-table-body-background-color: #141414;
|
||||
$vxe-table-row-striped-background-color: #1d1d1d;
|
||||
$vxe-table-row-hover-background-color: #1d1e1f;
|
||||
$vxe-table-row-hover-striped-background-color: #1e1e1e;
|
||||
$vxe-table-footer-background-color: #1d1e1f;
|
||||
$vxe-table-row-current-background-color: #302d2d;
|
||||
$vxe-table-column-current-background-color: #302d2d;
|
||||
$vxe-table-column-hover-background-color: #302d2d;
|
||||
$vxe-table-row-hover-current-background-color: #302d2d;
|
||||
$vxe-table-row-checkbox-checked-background-color: #3e3c37 !default;
|
||||
$vxe-table-row-hover-checkbox-checked-background-color: #615a4a !default;
|
||||
$vxe-table-menu-background-color: #1d1e1f;
|
||||
$vxe-table-border-width: 1px !default;
|
||||
$vxe-table-border-color: #4c4d4f !default;
|
||||
$vxe-table-fixed-left-scrolling-box-shadow: 8px 0px 10px -5px rgba(0, 0, 0, 0.12) !default;
|
||||
$vxe-table-fixed-right-scrolling-box-shadow: -8px 0px 10px -5px rgba(0, 0, 0, 0.12) !default;
|
||||
|
||||
$vxe-form-background-color: #141414;
|
||||
|
||||
/*pager*/
|
||||
$vxe-pager-background-color: #1d1e1f !default;
|
||||
$vxe-pager-perfect-background-color: #262727 !default;
|
||||
$vxe-pager-perfect-button-background-color: #a7a3a3 !default;
|
||||
|
||||
$vxe-input-background-color: #141414;
|
||||
$vxe-input-border-color: #4c4d4f !default;
|
||||
|
||||
$vxe-select-option-hover-background-color: #262626 !default;
|
||||
$vxe-select-panel-background-color: #141414 !default;
|
||||
$vxe-select-empty-color: #262626 !default;
|
||||
$vxe-optgroup-title-color: #909399 !default;
|
||||
|
||||
/*button*/
|
||||
$vxe-button-default-background-color: #262626;
|
||||
$vxe-button-dropdown-panel-background-color: #141414;
|
||||
|
||||
/*modal*/
|
||||
$vxe-modal-header-background-color: #141414;
|
||||
$vxe-modal-body-background-color: #141414;
|
||||
$vxe-modal-border-color: #3b3b3b;
|
||||
|
||||
/*pulldown*/
|
||||
$vxe-pulldown-panel-background-color: #262626 !default;
|
||||
|
||||
@import 'vxe-table/styles/index';
|
||||
@ -1,16 +0,0 @@
|
||||
// 修改样式变量
|
||||
// /*font*/
|
||||
// $vxe-font-size: 12px !default;
|
||||
// $vxe-font-size-medium: 16px !default;
|
||||
// $vxe-font-size-small: 14px !default;
|
||||
// $vxe-font-size-mini: 12px !default;
|
||||
/*color*/
|
||||
$vxe-primary-color: #409eff !default;
|
||||
$vxe-success-color: #67c23a !default;
|
||||
$vxe-info-color: #909399 !default;
|
||||
$vxe-warning-color: #e6a23c !default;
|
||||
$vxe-danger-color: #f56c6c !default;
|
||||
$vxe-disabled-color: #bfbfbf !default;
|
||||
$vxe-primary-disabled-color: #c0c4cc !default;
|
||||
|
||||
@import 'vxe-table/styles/index.scss';
|
||||