Browse Source

feat: 优化 captcha 代码

xingyu 2 years ago
parent
commit
aaf62982e2
33 changed files with 464 additions and 2747 deletions
  1. 1 0
      yudao-framework/yudao-spring-boot-starter-captcha/pom.xml
  2. 7 10
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/config/AjCaptchaServiceAutoConfiguration.java
  3. 2 3
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/config/AjCaptchaStorageAutoConfiguration.java
  4. 0 65
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/controller/CaptchaController.java
  5. 21 21
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/common/CaptchaBaseMapEnum.java
  6. 22 22
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/common/CaptchaTypeEnum.java
  7. 40 40
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/common/Const.java
  8. 20 25
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/common/RepCodeEnum.java
  9. 15 96
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/common/RequestModel.java
  10. 23 41
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/common/ResponseModel.java
  11. 13 160
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/vo/CaptchaVO.java
  12. 3 24
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/vo/PointVO.java
  13. 2 199
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/properties/AjCaptchaProperties.java
  14. 21 19
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/CaptchaCacheService.java
  15. 10 5
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/CaptchaService.java
  16. 29 29
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/AbstractCaptchaService.java
  17. 27 24
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/BlockPuzzleCaptchaServiceImpl.java
  18. 11 8
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/CaptchaCacheServiceMemImpl.java
  19. 6 8
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/CaptchaServiceFactory.java
  20. 3 0
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/ClickWordCaptchaServiceImpl.java
  21. 8 6
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/DefaultCaptchaServiceImpl.java
  22. 15 15
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/FrequencyLimitHandler.java
  23. 22 11
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/AESUtil.java
  24. 0 53
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/Base64Utils.java
  25. 22 23
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/CacheUtil.java
  26. 5 6
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/FileCopyUtils.java
  27. 9 12
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/ImageUtils.java
  28. 51 52
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/JsonUtil.java
  29. 7 4
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/MD5Util.java
  30. 11 10
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/RandomUtils.java
  31. 8 9
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/StreamUtils.java
  32. 0 1742
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/StringUtils.java
  33. 30 5
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java

+ 1 - 0
yudao-framework/yudao-spring-boot-starter-captcha/pom.xml

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

+ 7 - 10
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/config/AjCaptchaServiceAutoConfiguration.java

@@ -1,13 +1,12 @@
 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 com.anji.captcha.util.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -21,15 +20,14 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 
+@Slf4j
 @Configuration
 public class AjCaptchaServiceAutoConfiguration {
 
-    private static Logger logger = LoggerFactory.getLogger(AjCaptchaServiceAutoConfiguration.class);
-
     @Bean
     @ConditionalOnMissingBean
     public CaptchaService captchaService(AjCaptchaProperties prop) {
-        logger.info("自定义配置项:{}", prop.toString());
+        log.info("自定义配置项:{}", prop.toString());
         Properties config = new Properties();
         config.put(Const.CAPTCHA_CACHETYPE, prop.getCacheType().name());
         config.put(Const.CAPTCHA_WATER_MARK, prop.getWaterMark());
@@ -57,14 +55,13 @@ public class AjCaptchaServiceAutoConfiguration {
         config.put(Const.CAPTCHA_FONT_STYLE, prop.getFontStyle() + "");
         config.put(Const.CAPTCHA_WORD_COUNT, prop.getClickWordCount() + "");
 
-        if ((StringUtils.isNotBlank(prop.getJigsaw()) && prop.getJigsaw().startsWith("classpath:"))
-                || (StringUtils.isNotBlank(prop.getPicClick()) && prop.getPicClick().startsWith("classpath:"))) {
+        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());
         }
-        CaptchaService s = CaptchaServiceFactory.getInstance(config);
-        return s;
+        return CaptchaServiceFactory.getInstance(config);
     }
 
     private static void initializeBaseMap(String jigsaw, String picClick) {

+ 2 - 3
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/config/AjCaptchaStorageAutoConfiguration.java

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

+ 0 - 65
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/controller/CaptchaController.java

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

+ 21 - 21
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/common/CaptchaBaseMapEnum.java

@@ -1,46 +1,46 @@
 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 String codeValue;
-    private String codeDesc;
-
-    private CaptchaBaseMapEnum(String  codeValue, String codeDesc) {
-        this.codeValue = codeValue;
-        this.codeDesc = codeDesc;
-    }
-
-    public String   getCodeValue(){ return this.codeValue;}
+    ORIGINAL("ORIGINAL", "滑动拼图底图"),
+    SLIDING_BLOCK("SLIDING_BLOCK", "滑动拼图滑块底图"),
+    PIC_CLICK("PIC_CLICK", "文字点选底图");
 
-    public String getCodeDesc(){ return this.codeDesc;}
+    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;}
+    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){
+    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 boolean validateCodeValue(String codeValue) {
+        return parseFromCodeValue(codeValue) != null;
+    }
 
     //列出所有值字符串
-    public static String getString(){
+    public static String getString() {
         StringBuffer buffer = new StringBuffer();
-        for (CaptchaBaseMapEnum e : CaptchaBaseMapEnum.values()){
+        for (CaptchaBaseMapEnum e : CaptchaBaseMapEnum.values()) {
             buffer.append(e.codeValue).append("--").append(e.getCodeDesc()).append(", ");
         }
         buffer.deleteCharAt(buffer.lastIndexOf(","));

+ 22 - 22
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/common/CaptchaTypeEnum.java

@@ -1,52 +1,52 @@
 package com.anji.captcha.model.common;
 
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
 public enum CaptchaTypeEnum {
     /**
      * 滑块拼图.
      */
-    BLOCKPUZZLE("blockPuzzle","滑块拼图"),
+    BLOCKPUZZLE("blockPuzzle", "滑块拼图"),
     /**
      * 文字点选.
      */
-    CLICKWORD("clickWord","文字点选"),
+    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;}
+    DEFAULT("default", "默认");
 
-    public String getCodeDesc(){ return this.codeDesc;}
+    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;}
+    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){
+    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 boolean validateCodeValue(String codeValue) {
+        return parseFromCodeValue(codeValue) != null;
+    }
 
     //列出所有值字符串
-    public static String getString(){
-        StringBuffer buffer = new StringBuffer();
-        for (CaptchaTypeEnum e : CaptchaTypeEnum.values()){
+    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(","));

+ 40 - 40
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/common/Const.java

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

+ 20 - 25
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/common/RepCodeEnum.java

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

+ 15 - 96
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/common/RequestModel.java

@@ -6,23 +6,32 @@
  */
 package com.anji.captcha.model.common;
 
-import com.anji.captcha.util.StringUtils;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Data;
 
 import java.io.Serializable;
 import java.util.HashMap;
 import java.util.List;
 
+@Data
 public class RequestModel implements Serializable {
 
     private static final long serialVersionUID = -5800786065305114784L;
 
-    /**当前请求接口路径 /business/accessUser/login */
+    /**
+     * 当前请求接口路径 /business/accessUser/login
+     */
     private String servletPath;
 
-    /** {"reqData":{"password":"*****","userName":"admin"},"sign":"a304a7f296f565b6d2009797f68180f0","time":"1542456453355","token":""} */
+    /**
+     * {"reqData":{"password":"*****","userName":"admin"},"sign":"a304a7f296f565b6d2009797f68180f0","time":"1542456453355","token":""}
+     */
     private String requestString;
 
-    /** {"password":"****","userName":"admin"} */
+    /**
+     * {"password":"****","userName":"admin"}
+     */
     private HashMap reqData;
 
     private String token;
@@ -46,10 +55,11 @@ public class RequestModel implements Serializable {
 
     /**
      * 校验自身参数合法性
+     *
      * @return
      */
     public boolean isVaildateRequest() {
-        if (StringUtils.isBlank(sign) || StringUtils.isBlank(time)) {
+        if (StrUtil.isBlank(sign) || StrUtil.isBlank(time)) {
             return false;
         }
         return true;
@@ -63,95 +73,4 @@ public class RequestModel implements Serializable {
         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;
-    }
 }

+ 23 - 41
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/common/ResponseModel.java

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

+ 13 - 160
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/vo/CaptchaVO.java

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

+ 3 - 24
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/model/vo/PointVO.java

@@ -1,5 +1,7 @@
 package com.anji.captcha.model.vo;
 
+import lombok.Data;
+
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
@@ -9,6 +11,7 @@ import java.util.regex.Pattern;
 /**
  * Created by raodeming on 2020/5/16.
  */
+@Data
 public class PointVO {
     private String secretKey;
 
@@ -16,30 +19,6 @@ public class PointVO {
 
     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;

+ 2 - 199
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/properties/AjCaptchaProperties.java

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

+ 21 - 19
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/CaptchaCacheService.java

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

+ 10 - 5
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/CaptchaService.java

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

+ 29 - 29
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/AbstractCaptchaService.java

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

+ 27 - 24
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/BlockPuzzleCaptchaServiceImpl.java

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

+ 11 - 8
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/CaptchaCacheServiceMemImpl.java

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

+ 6 - 8
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/CaptchaServiceFactory.java

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

File diff suppressed because it is too large
+ 3 - 0
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/ClickWordCaptchaServiceImpl.java


+ 8 - 6
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/DefaultCaptchaServiceImpl.java

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

+ 15 - 15
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/service/impl/FrequencyLimitHandler.java

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

+ 22 - 11
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/AESUtil.java

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

+ 0 - 53
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/Base64Utils.java

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

+ 22 - 23
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/CacheUtil.java

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

+ 5 - 6
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/FileCopyUtils.java

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

+ 9 - 12
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/ImageUtils.java

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

+ 51 - 52
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/JsonUtil.java

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

+ 7 - 4
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/MD5Util.java

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

+ 11 - 10
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/RandomUtils.java

@@ -31,11 +31,10 @@ public class RandomUtils {
      * @return
      */
     public static String getRandomHan(String hanZi) {
-        String ch = hanZi.charAt(new Random().nextInt(hanZi.length())) + "";
-        return ch;
+        return hanZi.charAt(new Random().nextInt(hanZi.length())) + "";
     }
 
-    public static int getRandomInt(int bound){
+    public static int getRandomInt(int bound) {
         return ThreadLocalRandom.current().nextInt(bound);
     }
 
@@ -68,25 +67,27 @@ public class RandomUtils {
 
     /**
      * 随机范围内数字
+     *
      * @param startNum
      * @param endNum
      * @return
      */
     public static Integer getRandomInt(int startNum, int endNum) {
-        return ThreadLocalRandom.current().nextInt(endNum-startNum) + startNum;
+        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);
+    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();

+ 8 - 9
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/StreamUtils.java

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

+ 0 - 1742
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/com/anji/captcha/util/StringUtils.java

@@ -1,1742 +0,0 @@
-/*
- *Copyright © 2018 anji-plus
- *安吉加加信息技术有限公司
- *http://www.anji-plus.com
- *All rights reserved.
- */
-package com.anji.captcha.util;
-
-public class StringUtils {
-
-    /**
-     * The empty String <code>""</code>.
-     * @since 2.0
-     */
-    public static final String EMPTY = "";
-
-    /**
-     * Represents a failed index search.
-     * @since 2.1
-     */
-    public static final int INDEX_NOT_FOUND = -1;
-
-    /**
-     * <p>The maximum size to which the padding constant(s) can expand.</p>
-     */
-    private static final int PAD_LIMIT = 8192;
-
-    /**
-     * <p><code>StringUtils</code> instances should NOT be constructed in
-     * standard programming. Instead, the class should be used as
-     * <code>StringUtils.trim(" foo ");</code>.</p>
-     *
-     * <p>This constructor is public to permit tools that require a JavaBean
-     * instance to operate.</p>
-     */
-    public StringUtils() {
-        super();
-    }
-
-    // Empty checks
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Checks if a String is empty ("") or null.</p>
-     *
-     * <pre>
-     * StringUtils.isEmpty(null)      = true
-     * StringUtils.isEmpty("")        = true
-     * StringUtils.isEmpty(" ")       = false
-     * StringUtils.isEmpty("bob")     = false
-     * StringUtils.isEmpty("  bob  ") = false
-     * </pre>
-     *
-     * <p>NOTE: This method changed in Lang version 2.0.
-     * It no longer trims the String.
-     * That functionality is available in isBlank().</p>
-     *
-     * @param str  the String to check, may be null
-     * @return <code>true</code> if the String is empty or null
-     */
-    public static boolean isEmpty(String str) {
-        return str == null || str.length() == 0;
-    }
-
-    /**
-     * <p>Checks if a String is not empty ("") and not null.</p>
-     *
-     * <pre>
-     * StringUtils.isNotEmpty(null)      = false
-     * StringUtils.isNotEmpty("")        = false
-     * StringUtils.isNotEmpty(" ")       = true
-     * StringUtils.isNotEmpty("bob")     = true
-     * StringUtils.isNotEmpty("  bob  ") = true
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @return <code>true</code> if the String is not empty and not null
-     */
-    public static boolean isNotEmpty(String str) {
-        return !StringUtils.isEmpty(str);
-    }
-
-    /**
-     * <p>Checks if a String is whitespace, empty ("") or null.</p>
-     *
-     * <pre>
-     * StringUtils.isBlank(null)      = true
-     * StringUtils.isBlank("null")      = true
-     * StringUtils.isBlank("")        = true
-     * StringUtils.isBlank(" ")       = true
-     * StringUtils.isBlank("bob")     = false
-     * StringUtils.isBlank("  bob  ") = false
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @return <code>true</code> if the String is null, empty or whitespace
-     * @since 2.0
-     */
-    public static boolean isBlank(String str) {
-        int strLen;
-        if (str == null || (strLen = str.length()) == 0) {
-            return true;
-        }
-        if(equals("null", str.trim().toLowerCase())){
-            return true;
-        }
-        for (int i = 0; i < strLen; i++) {
-            if ((Character.isWhitespace(str.charAt(i)) == false)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * <p>Checks if a String is not empty (""), not null and not whitespace only.</p>
-     *
-     * <pre>
-     * StringUtils.isNotBlank(null)      = false
-     * StringUtils.isNotBlank("null")    = false
-     * StringUtils.isNotBlank("")        = false
-     * StringUtils.isNotBlank(" ")       = false
-     * StringUtils.isNotBlank("bob")     = true
-     * StringUtils.isNotBlank("  bob  ") = true
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @return <code>true</code> if the String is
-     *  not empty and not null and not whitespace
-     * @since 2.0
-     */
-    public static boolean isNotBlank(String str) {
-        return !StringUtils.isBlank(str);
-    }
-
-    // Trim
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Removes control characters (char &lt;= 32) from both
-     * ends of this String, handling <code>null</code> by returning
-     * an empty String ("").</p>
-     *
-     * <pre>
-     * StringUtils.clean(null)          = ""
-     * StringUtils.clean("")            = ""
-     * StringUtils.clean("abc")         = "abc"
-     * StringUtils.clean("    abc    ") = "abc"
-     * StringUtils.clean("     ")       = ""
-     * </pre>
-     *
-     * @see String#trim()
-     * @param str  the String to clean, may be null
-     * @return the trimmed text, never <code>null</code>
-     * @deprecated Use the clearer named {@link #trimToEmpty(String)}.
-     *             Method will be removed in Commons Lang 3.0.
-     */
-    public static String clean(String str) {
-        return str == null ? EMPTY : str.trim();
-    }
-
-    /**
-     * <p>Removes control characters (char &lt;= 32) from both
-     * ends of this String, handling <code>null</code> by returning
-     * <code>null</code>.</p>
-     *
-     * <p>The String is trimmed using {@link String#trim()}.
-     * Trim removes start and end characters &lt;= 32.
-     * To strip whitespace use {@link #strip(String)}.</p>
-     *
-     * <p>To trim your choice of characters, use the
-     * {@link #strip(String, String)} methods.</p>
-     *
-     * <pre>
-     * StringUtils.trim(null)          = null
-     * StringUtils.trim("")            = ""
-     * StringUtils.trim("     ")       = ""
-     * StringUtils.trim("abc")         = "abc"
-     * StringUtils.trim("    abc    ") = "abc"
-     * </pre>
-     *
-     * @param str  the String to be trimmed, may be null
-     * @return the trimmed string, <code>null</code> if null String input
-     */
-    public static String trim(String str) {
-        return str == null ? null : str.trim();
-    }
-
-    /**
-     * <p>Removes control characters (char &lt;= 32) from both
-     * ends of this String returning <code>null</code> if the String is
-     * empty ("") after the trim or if it is <code>null</code>.
-     *
-     * <p>The String is trimmed using {@link String#trim()}.
-     * Trim removes start and end characters &lt;= 32.
-     * To strip whitespace use {@link #stripToNull(String)}.</p>
-     *
-     * <pre>
-     * StringUtils.trimToNull(null)          = null
-     * StringUtils.trimToNull("")            = null
-     * StringUtils.trimToNull("     ")       = null
-     * StringUtils.trimToNull("abc")         = "abc"
-     * StringUtils.trimToNull("    abc    ") = "abc"
-     * </pre>
-     *
-     * @param str  the String to be trimmed, may be null
-     * @return the trimmed String,
-     *  <code>null</code> if only chars &lt;= 32, empty or null String input
-     * @since 2.0
-     */
-    public static String trimToNull(String str) {
-        String ts = trim(str);
-        return isEmpty(ts) ? null : ts;
-    }
-
-    /**
-     * <p>Removes control characters (char &lt;= 32) from both
-     * ends of this String returning an empty String ("") if the String
-     * is empty ("") after the trim or if it is <code>null</code>.
-     *
-     * <p>The String is trimmed using {@link String#trim()}.
-     * Trim removes start and end characters &lt;= 32.
-     * To strip whitespace use {@link #stripToEmpty(String)}.</p>
-     *
-     * <pre>
-     * StringUtils.trimToEmpty(null)          = ""
-     * StringUtils.trimToEmpty("")            = ""
-     * StringUtils.trimToEmpty("     ")       = ""
-     * StringUtils.trimToEmpty("abc")         = "abc"
-     * StringUtils.trimToEmpty("    abc    ") = "abc"
-     * </pre>
-     *
-     * @param str  the String to be trimmed, may be null
-     * @return the trimmed String, or an empty String if <code>null</code> input
-     * @since 2.0
-     */
-    public static String trimToEmpty(String str) {
-        return str == null ? EMPTY : str.trim();
-    }
-
-    // Stripping
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Strips whitespace from the start and end of a String.</p>
-     *
-     * <p>This is similar to {@link #trim(String)} but removes whitespace.
-     * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
-     *
-     * <p>A <code>null</code> input String returns <code>null</code>.</p>
-     *
-     * <pre>
-     * StringUtils.strip(null)     = null
-     * StringUtils.strip("")       = ""
-     * StringUtils.strip("   ")    = ""
-     * StringUtils.strip("abc")    = "abc"
-     * StringUtils.strip("  abc")  = "abc"
-     * StringUtils.strip("abc  ")  = "abc"
-     * StringUtils.strip(" abc ")  = "abc"
-     * StringUtils.strip(" ab c ") = "ab c"
-     * </pre>
-     *
-     * @param str  the String to remove whitespace from, may be null
-     * @return the stripped String, <code>null</code> if null String input
-     */
-    public static String strip(String str) {
-        return strip(str, null);
-    }
-
-    /**
-     * <p>Strips whitespace from the start and end of a String  returning
-     * <code>null</code> if the String is empty ("") after the strip.</p>
-     *
-     * <p>This is similar to {@link #trimToNull(String)} but removes whitespace.
-     * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
-     *
-     * <pre>
-     * StringUtils.stripToNull(null)     = null
-     * StringUtils.stripToNull("")       = null
-     * StringUtils.stripToNull("   ")    = null
-     * StringUtils.stripToNull("abc")    = "abc"
-     * StringUtils.stripToNull("  abc")  = "abc"
-     * StringUtils.stripToNull("abc  ")  = "abc"
-     * StringUtils.stripToNull(" abc ")  = "abc"
-     * StringUtils.stripToNull(" ab c ") = "ab c"
-     * </pre>
-     *
-     * @param str  the String to be stripped, may be null
-     * @return the stripped String,
-     *  <code>null</code> if whitespace, empty or null String input
-     * @since 2.0
-     */
-    public static String stripToNull(String str) {
-        if (str == null) {
-            return null;
-        }
-        str = strip(str, null);
-        return str.length() == 0 ? null : str;
-    }
-
-    /**
-     * <p>Strips whitespace from the start and end of a String  returning
-     * an empty String if <code>null</code> input.</p>
-     *
-     * <p>This is similar to {@link #trimToEmpty(String)} but removes whitespace.
-     * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
-     *
-     * <pre>
-     * StringUtils.stripToEmpty(null)     = ""
-     * StringUtils.stripToEmpty("")       = ""
-     * StringUtils.stripToEmpty("   ")    = ""
-     * StringUtils.stripToEmpty("abc")    = "abc"
-     * StringUtils.stripToEmpty("  abc")  = "abc"
-     * StringUtils.stripToEmpty("abc  ")  = "abc"
-     * StringUtils.stripToEmpty(" abc ")  = "abc"
-     * StringUtils.stripToEmpty(" ab c ") = "ab c"
-     * </pre>
-     *
-     * @param str  the String to be stripped, may be null
-     * @return the trimmed String, or an empty String if <code>null</code> input
-     * @since 2.0
-     */
-    public static String stripToEmpty(String str) {
-        return str == null ? EMPTY : strip(str, null);
-    }
-
-    /**
-     * <p>Strips any of a set of characters from the start and end of a String.
-     * This is similar to {@link String#trim()} but allows the characters
-     * to be stripped to be controlled.</p>
-     *
-     * <p>A <code>null</code> input String returns <code>null</code>.
-     * An empty string ("") input returns the empty string.</p>
-     *
-     * <p>If the stripChars String is <code>null</code>, whitespace is
-     * stripped as defined by {@link Character#isWhitespace(char)}.
-     * Alternatively use {@link #strip(String)}.</p>
-     *
-     * <pre>
-     * StringUtils.strip(null, *)          = null
-     * StringUtils.strip("", *)            = ""
-     * StringUtils.strip("abc", null)      = "abc"
-     * StringUtils.strip("  abc", null)    = "abc"
-     * StringUtils.strip("abc  ", null)    = "abc"
-     * StringUtils.strip(" abc ", null)    = "abc"
-     * StringUtils.strip("  abcyx", "xyz") = "  abc"
-     * </pre>
-     *
-     * @param str  the String to remove characters from, may be null
-     * @param stripChars  the characters to remove, null treated as whitespace
-     * @return the stripped String, <code>null</code> if null String input
-     */
-    public static String strip(String str, String stripChars) {
-        if (isEmpty(str)) {
-            return str;
-        }
-        str = stripStart(str, stripChars);
-        return stripEnd(str, stripChars);
-    }
-
-    /**
-     * <p>Strips any of a set of characters from the start of a String.</p>
-     *
-     * <p>A <code>null</code> input String returns <code>null</code>.
-     * An empty string ("") input returns the empty string.</p>
-     *
-     * <p>If the stripChars String is <code>null</code>, whitespace is
-     * stripped as defined by {@link Character#isWhitespace(char)}.</p>
-     *
-     * <pre>
-     * StringUtils.stripStart(null, *)          = null
-     * StringUtils.stripStart("", *)            = ""
-     * StringUtils.stripStart("abc", "")        = "abc"
-     * StringUtils.stripStart("abc", null)      = "abc"
-     * StringUtils.stripStart("  abc", null)    = "abc"
-     * StringUtils.stripStart("abc  ", null)    = "abc  "
-     * StringUtils.stripStart(" abc ", null)    = "abc "
-     * StringUtils.stripStart("yxabc  ", "xyz") = "abc  "
-     * </pre>
-     *
-     * @param str  the String to remove characters from, may be null
-     * @param stripChars  the characters to remove, null treated as whitespace
-     * @return the stripped String, <code>null</code> if null String input
-     */
-    public static String stripStart(String str, String stripChars) {
-        int strLen;
-        if (str == null || (strLen = str.length()) == 0) {
-            return str;
-        }
-        int start = 0;
-        if (stripChars == null) {
-            while ((start != strLen) && Character.isWhitespace(str.charAt(start))) {
-                start++;
-            }
-        } else if (stripChars.length() == 0) {
-            return str;
-        } else {
-            while ((start != strLen) && (stripChars.indexOf(str.charAt(start)) != INDEX_NOT_FOUND)) {
-                start++;
-            }
-        }
-        return str.substring(start);
-    }
-
-    /**
-     * <p>Strips any of a set of characters from the end of a String.</p>
-     *
-     * <p>A <code>null</code> input String returns <code>null</code>.
-     * An empty string ("") input returns the empty string.</p>
-     *
-     * <p>If the stripChars String is <code>null</code>, whitespace is
-     * stripped as defined by {@link Character#isWhitespace(char)}.</p>
-     *
-     * <pre>
-     * StringUtils.stripEnd(null, *)          = null
-     * StringUtils.stripEnd("", *)            = ""
-     * StringUtils.stripEnd("abc", "")        = "abc"
-     * StringUtils.stripEnd("abc", null)      = "abc"
-     * StringUtils.stripEnd("  abc", null)    = "  abc"
-     * StringUtils.stripEnd("abc  ", null)    = "abc"
-     * StringUtils.stripEnd(" abc ", null)    = " abc"
-     * StringUtils.stripEnd("  abcyx", "xyz") = "  abc"
-     * StringUtils.stripEnd("120.00", ".0")   = "12"
-     * </pre>
-     *
-     * @param str  the String to remove characters from, may be null
-     * @param stripChars  the set of characters to remove, null treated as whitespace
-     * @return the stripped String, <code>null</code> if null String input
-     */
-    public static String stripEnd(String str, String stripChars) {
-        int end;
-        if (str == null || (end = str.length()) == 0) {
-            return str;
-        }
-
-        if (stripChars == null) {
-            while ((end != 0) && Character.isWhitespace(str.charAt(end - 1))) {
-                end--;
-            }
-        } else if (stripChars.length() == 0) {
-            return str;
-        } else {
-            while ((end != 0) && (stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND)) {
-                end--;
-            }
-        }
-        return str.substring(0, end);
-    }
-
-    // StripAll
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Strips whitespace from the start and end of every String in an array.
-     * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
-     *
-     * <p>A new array is returned each time, except for length zero.
-     * A <code>null</code> array will return <code>null</code>.
-     * An empty array will return itself.
-     * A <code>null</code> array entry will be ignored.</p>
-     *
-     * <pre>
-     * StringUtils.stripAll(null)             = null
-     * StringUtils.stripAll([])               = []
-     * StringUtils.stripAll(["abc", "  abc"]) = ["abc", "abc"]
-     * StringUtils.stripAll(["abc  ", null])  = ["abc", null]
-     * </pre>
-     *
-     * @param strs  the array to remove whitespace from, may be null
-     * @return the stripped Strings, <code>null</code> if null array input
-     */
-    public static String[] stripAll(String[] strs) {
-        return stripAll(strs, null);
-    }
-
-    /**
-     * <p>Strips any of a set of characters from the start and end of every
-     * String in an array.</p>
-     * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
-     *
-     * <p>A new array is returned each time, except for length zero.
-     * A <code>null</code> array will return <code>null</code>.
-     * An empty array will return itself.
-     * A <code>null</code> array entry will be ignored.
-     * A <code>null</code> stripChars will strip whitespace as defined by
-     * {@link Character#isWhitespace(char)}.</p>
-     *
-     * <pre>
-     * StringUtils.stripAll(null, *)                = null
-     * StringUtils.stripAll([], *)                  = []
-     * StringUtils.stripAll(["abc", "  abc"], null) = ["abc", "abc"]
-     * StringUtils.stripAll(["abc  ", null], null)  = ["abc", null]
-     * StringUtils.stripAll(["abc  ", null], "yz")  = ["abc  ", null]
-     * StringUtils.stripAll(["yabcz", null], "yz")  = ["abc", null]
-     * </pre>
-     *
-     * @param strs  the array to remove characters from, may be null
-     * @param stripChars  the characters to remove, null treated as whitespace
-     * @return the stripped Strings, <code>null</code> if null array input
-     */
-    public static String[] stripAll(String[] strs, String stripChars) {
-        int strsLen;
-        if (strs == null || (strsLen = strs.length) == 0) {
-            return strs;
-        }
-        String[] newArr = new String[strsLen];
-        for (int i = 0; i < strsLen; i++) {
-            newArr[i] = strip(strs[i], stripChars);
-        }
-        return newArr;
-    }
-
-    // Equals
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Compares two Strings, returning <code>true</code> if they are equal.</p>
-     *
-     * <p><code>null</code>s are handled without exceptions. Two <code>null</code>
-     * references are considered to be equal. The comparison is case sensitive.</p>
-     *
-     * <pre>
-     * StringUtils.equals(null, null)   = true
-     * StringUtils.equals(null, "abc")  = false
-     * StringUtils.equals("abc", null)  = false
-     * StringUtils.equals("abc", "abc") = true
-     * StringUtils.equals("abc", "ABC") = false
-     * </pre>
-     *
-     * @see String#equals(Object)
-     * @param str1  the first String, may be null
-     * @param str2  the second String, may be null
-     * @return <code>true</code> if the Strings are equal, case sensitive, or
-     *  both <code>null</code>
-     */
-    public static boolean equals(String str1, String str2) {
-        return str1 == null ? str2 == null : str1.equals(str2);
-    }
-
-    /**
-     * <p>Compares two Strings, returning <code>true</code> if they are equal ignoring
-     * the case.</p>
-     *
-     * <p><code>null</code>s are handled without exceptions. Two <code>null</code>
-     * references are considered equal. Comparison is case insensitive.</p>
-     *
-     * <pre>
-     * StringUtils.equalsIgnoreCase(null, null)   = true
-     * StringUtils.equalsIgnoreCase(null, "abc")  = false
-     * StringUtils.equalsIgnoreCase("abc", null)  = false
-     * StringUtils.equalsIgnoreCase("abc", "abc") = true
-     * StringUtils.equalsIgnoreCase("abc", "ABC") = true
-     * </pre>
-     *
-     * @see String#equalsIgnoreCase(String)
-     * @param str1  the first String, may be null
-     * @param str2  the second String, may be null
-     * @return <code>true</code> if the Strings are equal, case insensitive, or
-     *  both <code>null</code>
-     */
-    public static boolean equalsIgnoreCase(String str1, String str2) {
-        return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2);
-    }
-
-    // IndexOf
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Finds the first index within a String, handling <code>null</code>.
-     * This method uses {@link String#indexOf(int)}.</p>
-     *
-     * <p>A <code>null</code> or empty ("") String will return <code>INDEX_NOT_FOUND (-1)</code>.</p>
-     *
-     * <pre>
-     * StringUtils.indexOf(null, *)         = -1
-     * StringUtils.indexOf("", *)           = -1
-     * StringUtils.indexOf("aabaabaa", 'a') = 0
-     * StringUtils.indexOf("aabaabaa", 'b') = 2
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchChar  the character to find
-     * @return the first index of the search character,
-     *  -1 if no match or <code>null</code> string input
-     * @since 2.0
-     */
-    public static int indexOf(String str, char searchChar) {
-        if (isEmpty(str)) {
-            return INDEX_NOT_FOUND;
-        }
-        return str.indexOf(searchChar);
-    }
-
-    /**
-     * <p>Finds the first index within a String from a start position,
-     * handling <code>null</code>.
-     * This method uses {@link String#indexOf(int, int)}.</p>
-     *
-     * <p>A <code>null</code> or empty ("") String will return <code>(INDEX_NOT_FOUND) -1</code>.
-     * A negative start position is treated as zero.
-     * A start position greater than the string length returns <code>-1</code>.</p>
-     *
-     * <pre>
-     * StringUtils.indexOf(null, *, *)          = -1
-     * StringUtils.indexOf("", *, *)            = -1
-     * StringUtils.indexOf("aabaabaa", 'b', 0)  = 2
-     * StringUtils.indexOf("aabaabaa", 'b', 3)  = 5
-     * StringUtils.indexOf("aabaabaa", 'b', 9)  = -1
-     * StringUtils.indexOf("aabaabaa", 'b', -1) = 2
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchChar  the character to find
-     * @param startPos  the start position, negative treated as zero
-     * @return the first index of the search character,
-     *  -1 if no match or <code>null</code> string input
-     * @since 2.0
-     */
-    public static int indexOf(String str, char searchChar, int startPos) {
-        if (isEmpty(str)) {
-            return INDEX_NOT_FOUND;
-        }
-        return str.indexOf(searchChar, startPos);
-    }
-
-    /**
-     * <p>Finds the first index within a String, handling <code>null</code>.
-     * This method uses {@link String#indexOf(String)}.</p>
-     *
-     * <p>A <code>null</code> String will return <code>-1</code>.</p>
-     *
-     * <pre>
-     * StringUtils.indexOf(null, *)          = -1
-     * StringUtils.indexOf(*, null)          = -1
-     * StringUtils.indexOf("", "")           = 0
-     * StringUtils.indexOf("", *)            = -1 (except when * = "")
-     * StringUtils.indexOf("aabaabaa", "a")  = 0
-     * StringUtils.indexOf("aabaabaa", "b")  = 2
-     * StringUtils.indexOf("aabaabaa", "ab") = 1
-     * StringUtils.indexOf("aabaabaa", "")   = 0
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStr  the String to find, may be null
-     * @return the first index of the search String,
-     *  -1 if no match or <code>null</code> string input
-     * @since 2.0
-     */
-    public static int indexOf(String str, String searchStr) {
-        if (str == null || searchStr == null) {
-            return INDEX_NOT_FOUND;
-        }
-        return str.indexOf(searchStr);
-    }
-
-    /**
-     * <p>Finds the n-th index within a String, handling <code>null</code>.
-     * This method uses {@link String#indexOf(String)}.</p>
-     *
-     * <p>A <code>null</code> String will return <code>-1</code>.</p>
-     *
-     * <pre>
-     * StringUtils.ordinalIndexOf(null, *, *)          = -1
-     * StringUtils.ordinalIndexOf(*, null, *)          = -1
-     * StringUtils.ordinalIndexOf("", "", *)           = 0
-     * StringUtils.ordinalIndexOf("aabaabaa", "a", 1)  = 0
-     * StringUtils.ordinalIndexOf("aabaabaa", "a", 2)  = 1
-     * StringUtils.ordinalIndexOf("aabaabaa", "b", 1)  = 2
-     * StringUtils.ordinalIndexOf("aabaabaa", "b", 2)  = 5
-     * StringUtils.ordinalIndexOf("aabaabaa", "ab", 1) = 1
-     * StringUtils.ordinalIndexOf("aabaabaa", "ab", 2) = 4
-     * StringUtils.ordinalIndexOf("aabaabaa", "", 1)   = 0
-     * StringUtils.ordinalIndexOf("aabaabaa", "", 2)   = 0
-     * </pre>
-     *
-     * <p>Note that 'head(String str, int n)' may be implemented as: </p>
-     *
-     * <pre>
-     *   str.substring(0, lastOrdinalIndexOf(str, "\n", n))
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStr  the String to find, may be null
-     * @param ordinal  the n-th <code>searchStr</code> to find
-     * @return the n-th index of the search String,
-     *  <code>-1</code> (<code>INDEX_NOT_FOUND</code>) if no match or <code>null</code> string input
-     * @since 2.1
-     */
-    public static int ordinalIndexOf(String str, String searchStr, int ordinal) {
-        return ordinalIndexOf(str, searchStr, ordinal, false);
-    }
-
-    /**
-     * <p>Finds the n-th index within a String, handling <code>null</code>.
-     * This method uses {@link String#indexOf(String)}.</p>
-     *
-     * <p>A <code>null</code> String will return <code>-1</code>.</p>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStr  the String to find, may be null
-     * @param ordinal  the n-th <code>searchStr</code> to find
-     * @param lastIndex true if lastOrdinalIndexOf() otherwise false if ordinalIndexOf()
-     * @return the n-th index of the search String,
-     *  <code>-1</code> (<code>INDEX_NOT_FOUND</code>) if no match or <code>null</code> string input
-     */
-    // Shared code between ordinalIndexOf(String,String,int) and lastOrdinalIndexOf(String,String,int)
-    private static int ordinalIndexOf(String str, String searchStr, int ordinal, boolean lastIndex) {
-        if (str == null || searchStr == null || ordinal <= 0) {
-            return INDEX_NOT_FOUND;
-        }
-        if (searchStr.length() == 0) {
-            return lastIndex ? str.length() : 0;
-        }
-        int found = 0;
-        int index = lastIndex ? str.length() : INDEX_NOT_FOUND;
-        do {
-            if(lastIndex) {
-                index = str.lastIndexOf(searchStr, index - 1);
-            } else {
-                index = str.indexOf(searchStr, index + 1);
-            }
-            if (index < 0) {
-                return index;
-            }
-            found++;
-        } while (found < ordinal);
-        return index;
-    }
-
-    /**
-     * <p>Finds the first index within a String, handling <code>null</code>.
-     * This method uses {@link String#indexOf(String, int)}.</p>
-     *
-     * <p>A <code>null</code> String will return <code>-1</code>.
-     * A negative start position is treated as zero.
-     * An empty ("") search String always matches.
-     * A start position greater than the string length only matches
-     * an empty search String.</p>
-     *
-     * <pre>
-     * StringUtils.indexOf(null, *, *)          = -1
-     * StringUtils.indexOf(*, null, *)          = -1
-     * StringUtils.indexOf("", "", 0)           = 0
-     * StringUtils.indexOf("", *, 0)            = -1 (except when * = "")
-     * StringUtils.indexOf("aabaabaa", "a", 0)  = 0
-     * StringUtils.indexOf("aabaabaa", "b", 0)  = 2
-     * StringUtils.indexOf("aabaabaa", "ab", 0) = 1
-     * StringUtils.indexOf("aabaabaa", "b", 3)  = 5
-     * StringUtils.indexOf("aabaabaa", "b", 9)  = -1
-     * StringUtils.indexOf("aabaabaa", "b", -1) = 2
-     * StringUtils.indexOf("aabaabaa", "", 2)   = 2
-     * StringUtils.indexOf("abc", "", 9)        = 3
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStr  the String to find, may be null
-     * @param startPos  the start position, negative treated as zero
-     * @return the first index of the search String,
-     *  -1 if no match or <code>null</code> string input
-     * @since 2.0
-     */
-    public static int indexOf(String str, String searchStr, int startPos) {
-        if (str == null || searchStr == null) {
-            return INDEX_NOT_FOUND;
-        }
-        // JDK1.2/JDK1.3 have a bug, when startPos > str.length for "", hence
-        if (searchStr.length() == 0 && startPos >= str.length()) {
-            return str.length();
-        }
-        return str.indexOf(searchStr, startPos);
-    }
-
-    /**
-     * <p>Case in-sensitive find of the first index within a String.</p>
-     *
-     * <p>A <code>null</code> String will return <code>-1</code>.
-     * A negative start position is treated as zero.
-     * An empty ("") search String always matches.
-     * A start position greater than the string length only matches
-     * an empty search String.</p>
-     *
-     * <pre>
-     * StringUtils.indexOfIgnoreCase(null, *)          = -1
-     * StringUtils.indexOfIgnoreCase(*, null)          = -1
-     * StringUtils.indexOfIgnoreCase("", "")           = 0
-     * StringUtils.indexOfIgnoreCase("aabaabaa", "a")  = 0
-     * StringUtils.indexOfIgnoreCase("aabaabaa", "b")  = 2
-     * StringUtils.indexOfIgnoreCase("aabaabaa", "ab") = 1
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStr  the String to find, may be null
-     * @return the first index of the search String,
-     *  -1 if no match or <code>null</code> string input
-     * @since 2.5
-     */
-    public static int indexOfIgnoreCase(String str, String searchStr) {
-        return indexOfIgnoreCase(str, searchStr, 0);
-    }
-
-    /**
-     * <p>Case in-sensitive find of the first index within a String
-     * from the specified position.</p>
-     *
-     * <p>A <code>null</code> String will return <code>-1</code>.
-     * A negative start position is treated as zero.
-     * An empty ("") search String always matches.
-     * A start position greater than the string length only matches
-     * an empty search String.</p>
-     *
-     * <pre>
-     * StringUtils.indexOfIgnoreCase(null, *, *)          = -1
-     * StringUtils.indexOfIgnoreCase(*, null, *)          = -1
-     * StringUtils.indexOfIgnoreCase("", "", 0)           = 0
-     * StringUtils.indexOfIgnoreCase("aabaabaa", "A", 0)  = 0
-     * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 0)  = 2
-     * StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
-     * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 3)  = 5
-     * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 9)  = -1
-     * StringUtils.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
-     * StringUtils.indexOfIgnoreCase("aabaabaa", "", 2)   = 2
-     * StringUtils.indexOfIgnoreCase("abc", "", 9)        = 3
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStr  the String to find, may be null
-     * @param startPos  the start position, negative treated as zero
-     * @return the first index of the search String,
-     *  -1 if no match or <code>null</code> string input
-     * @since 2.5
-     */
-    public static int indexOfIgnoreCase(String str, String searchStr, int startPos) {
-        if (str == null || searchStr == null) {
-            return INDEX_NOT_FOUND;
-        }
-        if (startPos < 0) {
-            startPos = 0;
-        }
-        int endLimit = (str.length() - searchStr.length()) + 1;
-        if (startPos > endLimit) {
-            return INDEX_NOT_FOUND;
-        }
-        if (searchStr.length() == 0) {
-            return startPos;
-        }
-        for (int i = startPos; i < endLimit; i++) {
-            if (str.regionMatches(true, i, searchStr, 0, searchStr.length())) {
-                return i;
-            }
-        }
-        return INDEX_NOT_FOUND;
-    }
-
-    // LastIndexOf
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Finds the last index within a String, handling <code>null</code>.
-     * This method uses {@link String#lastIndexOf(int)}.</p>
-     *
-     * <p>A <code>null</code> or empty ("") String will return <code>-1</code>.</p>
-     *
-     * <pre>
-     * StringUtils.lastIndexOf(null, *)         = -1
-     * StringUtils.lastIndexOf("", *)           = -1
-     * StringUtils.lastIndexOf("aabaabaa", 'a') = 7
-     * StringUtils.lastIndexOf("aabaabaa", 'b') = 5
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchChar  the character to find
-     * @return the last index of the search character,
-     *  -1 if no match or <code>null</code> string input
-     * @since 2.0
-     */
-    public static int lastIndexOf(String str, char searchChar) {
-        if (isEmpty(str)) {
-            return INDEX_NOT_FOUND;
-        }
-        return str.lastIndexOf(searchChar);
-    }
-
-    /**
-     * <p>Finds the last index within a String from a start position,
-     * handling <code>null</code>.
-     * This method uses {@link String#lastIndexOf(int, int)}.</p>
-     *
-     * <p>A <code>null</code> or empty ("") String will return <code>-1</code>.
-     * A negative start position returns <code>-1</code>.
-     * A start position greater than the string length searches the whole string.</p>
-     *
-     * <pre>
-     * StringUtils.lastIndexOf(null, *, *)          = -1
-     * StringUtils.lastIndexOf("", *,  *)           = -1
-     * StringUtils.lastIndexOf("aabaabaa", 'b', 8)  = 5
-     * StringUtils.lastIndexOf("aabaabaa", 'b', 4)  = 2
-     * StringUtils.lastIndexOf("aabaabaa", 'b', 0)  = -1
-     * StringUtils.lastIndexOf("aabaabaa", 'b', 9)  = 5
-     * StringUtils.lastIndexOf("aabaabaa", 'b', -1) = -1
-     * StringUtils.lastIndexOf("aabaabaa", 'a', 0)  = 0
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchChar  the character to find
-     * @param startPos  the start position
-     * @return the last index of the search character,
-     *  -1 if no match or <code>null</code> string input
-     * @since 2.0
-     */
-    public static int lastIndexOf(String str, char searchChar, int startPos) {
-        if (isEmpty(str)) {
-            return INDEX_NOT_FOUND;
-        }
-        return str.lastIndexOf(searchChar, startPos);
-    }
-
-    /**
-     * <p>Finds the last index within a String, handling <code>null</code>.
-     * This method uses {@link String#lastIndexOf(String)}.</p>
-     *
-     * <p>A <code>null</code> String will return <code>-1</code>.</p>
-     *
-     * <pre>
-     * StringUtils.lastIndexOf(null, *)          = -1
-     * StringUtils.lastIndexOf(*, null)          = -1
-     * StringUtils.lastIndexOf("", "")           = 0
-     * StringUtils.lastIndexOf("aabaabaa", "a")  = 7
-     * StringUtils.lastIndexOf("aabaabaa", "b")  = 5
-     * StringUtils.lastIndexOf("aabaabaa", "ab") = 4
-     * StringUtils.lastIndexOf("aabaabaa", "")   = 8
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStr  the String to find, may be null
-     * @return the last index of the search String,
-     *  -1 if no match or <code>null</code> string input
-     * @since 2.0
-     */
-    public static int lastIndexOf(String str, String searchStr) {
-        if (str == null || searchStr == null) {
-            return INDEX_NOT_FOUND;
-        }
-        return str.lastIndexOf(searchStr);
-    }
-
-    /**
-     * <p>Finds the n-th last index within a String, handling <code>null</code>.
-     * This method uses {@link String#lastIndexOf(String)}.</p>
-     *
-     * <p>A <code>null</code> String will return <code>-1</code>.</p>
-     *
-     * <pre>
-     * StringUtils.lastOrdinalIndexOf(null, *, *)          = -1
-     * StringUtils.lastOrdinalIndexOf(*, null, *)          = -1
-     * StringUtils.lastOrdinalIndexOf("", "", *)           = 0
-     * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 1)  = 7
-     * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 2)  = 6
-     * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 1)  = 5
-     * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 2)  = 2
-     * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 1) = 4
-     * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 2) = 1
-     * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 1)   = 8
-     * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 2)   = 8
-     * </pre>
-     *
-     * <p>Note that 'tail(String str, int n)' may be implemented as: </p>
-     *
-     * <pre>
-     *   str.substring(lastOrdinalIndexOf(str, "\n", n) + 1)
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStr  the String to find, may be null
-     * @param ordinal  the n-th last <code>searchStr</code> to find
-     * @return the n-th last index of the search String,
-     *  <code>-1</code> (<code>INDEX_NOT_FOUND</code>) if no match or <code>null</code> string input
-     * @since 2.5
-     */
-    public static int lastOrdinalIndexOf(String str, String searchStr, int ordinal) {
-        return ordinalIndexOf(str, searchStr, ordinal, true);
-    }
-
-    /**
-     * <p>Finds the first index within a String, handling <code>null</code>.
-     * This method uses {@link String#lastIndexOf(String, int)}.</p>
-     *
-     * <p>A <code>null</code> String will return <code>-1</code>.
-     * A negative start position returns <code>-1</code>.
-     * An empty ("") search String always matches unless the start position is negative.
-     * A start position greater than the string length searches the whole string.</p>
-     *
-     * <pre>
-     * StringUtils.lastIndexOf(null, *, *)          = -1
-     * StringUtils.lastIndexOf(*, null, *)          = -1
-     * StringUtils.lastIndexOf("aabaabaa", "a", 8)  = 7
-     * StringUtils.lastIndexOf("aabaabaa", "b", 8)  = 5
-     * StringUtils.lastIndexOf("aabaabaa", "ab", 8) = 4
-     * StringUtils.lastIndexOf("aabaabaa", "b", 9)  = 5
-     * StringUtils.lastIndexOf("aabaabaa", "b", -1) = -1
-     * StringUtils.lastIndexOf("aabaabaa", "a", 0)  = 0
-     * StringUtils.lastIndexOf("aabaabaa", "b", 0)  = -1
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStr  the String to find, may be null
-     * @param startPos  the start position, negative treated as zero
-     * @return the first index of the search String,
-     *  -1 if no match or <code>null</code> string input
-     * @since 2.0
-     */
-    public static int lastIndexOf(String str, String searchStr, int startPos) {
-        if (str == null || searchStr == null) {
-            return INDEX_NOT_FOUND;
-        }
-        return str.lastIndexOf(searchStr, startPos);
-    }
-
-    /**
-     * <p>Case in-sensitive find of the last index within a String.</p>
-     *
-     * <p>A <code>null</code> String will return <code>-1</code>.
-     * A negative start position returns <code>-1</code>.
-     * An empty ("") search String always matches unless the start position is negative.
-     * A start position greater than the string length searches the whole string.</p>
-     *
-     * <pre>
-     * StringUtils.lastIndexOfIgnoreCase(null, *)          = -1
-     * StringUtils.lastIndexOfIgnoreCase(*, null)          = -1
-     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A")  = 7
-     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B")  = 5
-     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB") = 4
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStr  the String to find, may be null
-     * @return the first index of the search String,
-     *  -1 if no match or <code>null</code> string input
-     * @since 2.5
-     */
-    public static int lastIndexOfIgnoreCase(String str, String searchStr) {
-        if (str == null || searchStr == null) {
-            return INDEX_NOT_FOUND;
-        }
-        return lastIndexOfIgnoreCase(str, searchStr, str.length());
-    }
-
-    /**
-     * <p>Case in-sensitive find of the last index within a String
-     * from the specified position.</p>
-     *
-     * <p>A <code>null</code> String will return <code>-1</code>.
-     * A negative start position returns <code>-1</code>.
-     * An empty ("") search String always matches unless the start position is negative.
-     * A start position greater than the string length searches the whole string.</p>
-     *
-     * <pre>
-     * StringUtils.lastIndexOfIgnoreCase(null, *, *)          = -1
-     * StringUtils.lastIndexOfIgnoreCase(*, null, *)          = -1
-     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 8)  = 7
-     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 8)  = 5
-     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB", 8) = 4
-     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 9)  = 5
-     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", -1) = -1
-     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 0)  = 0
-     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 0)  = -1
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStr  the String to find, may be null
-     * @param startPos  the start position
-     * @return the first index of the search String,
-     *  -1 if no match or <code>null</code> string input
-     * @since 2.5
-     */
-    public static int lastIndexOfIgnoreCase(String str, String searchStr, int startPos) {
-        if (str == null || searchStr == null) {
-            return INDEX_NOT_FOUND;
-        }
-        if (startPos > (str.length() - searchStr.length())) {
-            startPos = str.length() - searchStr.length();
-        }
-        if (startPos < 0) {
-            return INDEX_NOT_FOUND;
-        }
-        if (searchStr.length() == 0) {
-            return startPos;
-        }
-
-        for (int i = startPos; i >= 0; i--) {
-            if (str.regionMatches(true, i, searchStr, 0, searchStr.length())) {
-                return i;
-            }
-        }
-        return INDEX_NOT_FOUND;
-    }
-
-    // Contains
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Checks if String contains a search character, handling <code>null</code>.
-     * This method uses {@link String#indexOf(int)}.</p>
-     *
-     * <p>A <code>null</code> or empty ("") String will return <code>false</code>.</p>
-     *
-     * <pre>
-     * StringUtils.contains(null, *)    = false
-     * StringUtils.contains("", *)      = false
-     * StringUtils.contains("abc", 'a') = true
-     * StringUtils.contains("abc", 'z') = false
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchChar  the character to find
-     * @return true if the String contains the search character,
-     *  false if not or <code>null</code> string input
-     * @since 2.0
-     */
-    public static boolean contains(String str, char searchChar) {
-        if (isEmpty(str)) {
-            return false;
-        }
-        return str.indexOf(searchChar) >= 0;
-    }
-
-    /**
-     * <p>Checks if String contains a search String, handling <code>null</code>.
-     * This method uses {@link String#indexOf(String)}.</p>
-     *
-     * <p>A <code>null</code> String will return <code>false</code>.</p>
-     *
-     * <pre>
-     * StringUtils.contains(null, *)     = false
-     * StringUtils.contains(*, null)     = false
-     * StringUtils.contains("", "")      = true
-     * StringUtils.contains("abc", "")   = true
-     * StringUtils.contains("abc", "a")  = true
-     * StringUtils.contains("abc", "z")  = false
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStr  the String to find, may be null
-     * @return true if the String contains the search String,
-     *  false if not or <code>null</code> string input
-     * @since 2.0
-     */
-    public static boolean contains(String str, String searchStr) {
-        if (str == null || searchStr == null) {
-            return false;
-        }
-        return str.indexOf(searchStr) >= 0;
-    }
-
-    /**
-     * <p>Checks if String contains a search String irrespective of case,
-     * handling <code>null</code>. Case-insensitivity is defined as by
-     * {@link String#equalsIgnoreCase(String)}.
-     *
-     * <p>A <code>null</code> String will return <code>false</code>.</p>
-     *
-     * <pre>
-     * StringUtils.contains(null, *) = false
-     * StringUtils.contains(*, null) = false
-     * StringUtils.contains("", "") = true
-     * StringUtils.contains("abc", "") = true
-     * StringUtils.contains("abc", "a") = true
-     * StringUtils.contains("abc", "z") = false
-     * StringUtils.contains("abc", "A") = true
-     * StringUtils.contains("abc", "Z") = false
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStr  the String to find, may be null
-     * @return true if the String contains the search String irrespective of
-     * case or false if not or <code>null</code> string input
-     */
-    public static boolean containsIgnoreCase(String str, String searchStr) {
-        if (str == null || searchStr == null) {
-            return false;
-        }
-        int len = searchStr.length();
-        int max = str.length() - len;
-        for (int i = 0; i <= max; i++) {
-            if (str.regionMatches(true, i, searchStr, 0, len)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-
-    // IndexOfAny strings
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Find the first index of any of a set of potential substrings.</p>
-     *
-     * <p>A <code>null</code> String will return <code>-1</code>.
-     * A <code>null</code> or zero length search array will return <code>-1</code>.
-     * A <code>null</code> search array entry will be ignored, but a search
-     * array containing "" will return <code>0</code> if <code>str</code> is not
-     * null. This method uses {@link String#indexOf(String)}.</p>
-     *
-     * <pre>
-     * StringUtils.indexOfAny(null, *)                     = -1
-     * StringUtils.indexOfAny(*, null)                     = -1
-     * StringUtils.indexOfAny(*, [])                       = -1
-     * StringUtils.indexOfAny("zzabyycdxx", ["ab","cd"])   = 2
-     * StringUtils.indexOfAny("zzabyycdxx", ["cd","ab"])   = 2
-     * StringUtils.indexOfAny("zzabyycdxx", ["mn","op"])   = -1
-     * StringUtils.indexOfAny("zzabyycdxx", ["zab","aby"]) = 1
-     * StringUtils.indexOfAny("zzabyycdxx", [""])          = 0
-     * StringUtils.indexOfAny("", [""])                    = 0
-     * StringUtils.indexOfAny("", ["a"])                   = -1
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStrs  the Strings to search for, may be null
-     * @return the first index of any of the searchStrs in str, -1 if no match
-     */
-    public static int indexOfAny(String str, String[] searchStrs) {
-        if ((str == null) || (searchStrs == null)) {
-            return INDEX_NOT_FOUND;
-        }
-        int sz = searchStrs.length;
-
-        // String's can't have a MAX_VALUEth index.
-        int ret = Integer.MAX_VALUE;
-
-        int tmp = 0;
-        for (int i = 0; i < sz; i++) {
-            String search = searchStrs[i];
-            if (search == null) {
-                continue;
-            }
-            tmp = str.indexOf(search);
-            if (tmp == INDEX_NOT_FOUND) {
-                continue;
-            }
-
-            if (tmp < ret) {
-                ret = tmp;
-            }
-        }
-
-        return (ret == Integer.MAX_VALUE) ? INDEX_NOT_FOUND : ret;
-    }
-
-    /**
-     * <p>Find the latest index of any of a set of potential substrings.</p>
-     *
-     * <p>A <code>null</code> String will return <code>-1</code>.
-     * A <code>null</code> search array will return <code>-1</code>.
-     * A <code>null</code> or zero length search array entry will be ignored,
-     * but a search array containing "" will return the length of <code>str</code>
-     * if <code>str</code> is not null. This method uses {@link String#indexOf(String)}</p>
-     *
-     * <pre>
-     * StringUtils.lastIndexOfAny(null, *)                   = -1
-     * StringUtils.lastIndexOfAny(*, null)                   = -1
-     * StringUtils.lastIndexOfAny(*, [])                     = -1
-     * StringUtils.lastIndexOfAny(*, [null])                 = -1
-     * StringUtils.lastIndexOfAny("zzabyycdxx", ["ab","cd"]) = 6
-     * StringUtils.lastIndexOfAny("zzabyycdxx", ["cd","ab"]) = 6
-     * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn","op"]) = -1
-     * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn","op"]) = -1
-     * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn",""])   = 10
-     * </pre>
-     *
-     * @param str  the String to check, may be null
-     * @param searchStrs  the Strings to search for, may be null
-     * @return the last index of any of the Strings, -1 if no match
-     */
-    public static int lastIndexOfAny(String str, String[] searchStrs) {
-        if ((str == null) || (searchStrs == null)) {
-            return INDEX_NOT_FOUND;
-        }
-        int sz = searchStrs.length;
-        int ret = INDEX_NOT_FOUND;
-        int tmp = 0;
-        for (int i = 0; i < sz; i++) {
-            String search = searchStrs[i];
-            if (search == null) {
-                continue;
-            }
-            tmp = str.lastIndexOf(search);
-            if (tmp > ret) {
-                ret = tmp;
-            }
-        }
-        return ret;
-    }
-
-    // Substring
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Gets a substring from the specified String avoiding exceptions.</p>
-     *
-     * <p>A negative start position can be used to start <code>n</code>
-     * characters from the end of the String.</p>
-     *
-     * <p>A <code>null</code> String will return <code>null</code>.
-     * An empty ("") String will return "".</p>
-     *
-     * <pre>
-     * StringUtils.substring(null, *)   = null
-     * StringUtils.substring("", *)     = ""
-     * StringUtils.substring("abc", 0)  = "abc"
-     * StringUtils.substring("abc", 2)  = "c"
-     * StringUtils.substring("abc", 4)  = ""
-     * StringUtils.substring("abc", -2) = "bc"
-     * StringUtils.substring("abc", -4) = "abc"
-     * </pre>
-     *
-     * @param str  the String to get the substring from, may be null
-     * @param start  the position to start from, negative means
-     *  count back from the end of the String by this many characters
-     * @return substring from start position, <code>null</code> if null String input
-     */
-    public static String substring(String str, int start) {
-        if (str == null) {
-            return null;
-        }
-
-        // handle negatives, which means last n characters
-        if (start < 0) {
-            start = str.length() + start; // remember start is negative
-        }
-
-        if (start < 0) {
-            start = 0;
-        }
-        if (start > str.length()) {
-            return EMPTY;
-        }
-
-        return str.substring(start);
-    }
-
-    /**
-     * <p>Gets a substring from the specified String avoiding exceptions.</p>
-     *
-     * <p>A negative start position can be used to start/end <code>n</code>
-     * characters from the end of the String.</p>
-     *
-     * <p>The returned substring starts with the character in the <code>start</code>
-     * position and ends before the <code>end</code> position. All position counting is
-     * zero-based -- i.e., to start at the beginning of the string use
-     * <code>start = 0</code>. Negative start and end positions can be used to
-     * specify offsets relative to the end of the String.</p>
-     *
-     * <p>If <code>start</code> is not strictly to the left of <code>end</code>, ""
-     * is returned.</p>
-     *
-     * <pre>
-     * StringUtils.substring(null, *, *)    = null
-     * StringUtils.substring("", * ,  *)    = "";
-     * StringUtils.substring("abc", 0, 2)   = "ab"
-     * StringUtils.substring("abc", 2, 0)   = ""
-     * StringUtils.substring("abc", 2, 4)   = "c"
-     * StringUtils.substring("abc", 4, 6)   = ""
-     * StringUtils.substring("abc", 2, 2)   = ""
-     * StringUtils.substring("abc", -2, -1) = "b"
-     * StringUtils.substring("abc", -4, 2)  = "ab"
-     * </pre>
-     *
-     * @param str  the String to get the substring from, may be null
-     * @param start  the position to start from, negative means
-     *  count back from the end of the String by this many characters
-     * @param end  the position to end at (exclusive), negative means
-     *  count back from the end of the String by this many characters
-     * @return substring from start position to end positon,
-     *  <code>null</code> if null String input
-     */
-    public static String substring(String str, int start, int end) {
-        if (str == null) {
-            return null;
-        }
-
-        // handle negatives
-        if (end < 0) {
-            end = str.length() + end; // remember end is negative
-        }
-        if (start < 0) {
-            start = str.length() + start; // remember start is negative
-        }
-
-        // check length next
-        if (end > str.length()) {
-            end = str.length();
-        }
-
-        // if start is greater than end, return ""
-        if (start > end) {
-            return EMPTY;
-        }
-
-        if (start < 0) {
-            start = 0;
-        }
-        if (end < 0) {
-            end = 0;
-        }
-
-        return str.substring(start, end);
-    }
-
-    // Left/Right/Mid
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Gets the leftmost <code>len</code> characters of a String.</p>
-     *
-     * <p>If <code>len</code> characters are not available, or the
-     * String is <code>null</code>, the String will be returned without
-     * an exception. An empty String is returned if len is negative.</p>
-     *
-     * <pre>
-     * StringUtils.left(null, *)    = null
-     * StringUtils.left(*, -ve)     = ""
-     * StringUtils.left("", *)      = ""
-     * StringUtils.left("abc", 0)   = ""
-     * StringUtils.left("abc", 2)   = "ab"
-     * StringUtils.left("abc", 4)   = "abc"
-     * </pre>
-     *
-     * @param str  the String to get the leftmost characters from, may be null
-     * @param len  the length of the required String
-     * @return the leftmost characters, <code>null</code> if null String input
-     */
-    public static String left(String str, int len) {
-        if (str == null) {
-            return null;
-        }
-        if (len < 0) {
-            return EMPTY;
-        }
-        if (str.length() <= len) {
-            return str;
-        }
-        return str.substring(0, len);
-    }
-
-    /**
-     * <p>Gets the rightmost <code>len</code> characters of a String.</p>
-     *
-     * <p>If <code>len</code> characters are not available, or the String
-     * is <code>null</code>, the String will be returned without an
-     * an exception. An empty String is returned if len is negative.</p>
-     *
-     * <pre>
-     * StringUtils.right(null, *)    = null
-     * StringUtils.right(*, -ve)     = ""
-     * StringUtils.right("", *)      = ""
-     * StringUtils.right("abc", 0)   = ""
-     * StringUtils.right("abc", 2)   = "bc"
-     * StringUtils.right("abc", 4)   = "abc"
-     * </pre>
-     *
-     * @param str  the String to get the rightmost characters from, may be null
-     * @param len  the length of the required String
-     * @return the rightmost characters, <code>null</code> if null String input
-     */
-    public static String right(String str, int len) {
-        if (str == null) {
-            return null;
-        }
-        if (len < 0) {
-            return EMPTY;
-        }
-        if (str.length() <= len) {
-            return str;
-        }
-        return str.substring(str.length() - len);
-    }
-
-    /**
-     * <p>Gets <code>len</code> characters from the middle of a String.</p>
-     *
-     * <p>If <code>len</code> characters are not available, the remainder
-     * of the String will be returned without an exception. If the
-     * String is <code>null</code>, <code>null</code> will be returned.
-     * An empty String is returned if len is negative or exceeds the
-     * length of <code>str</code>.</p>
-     *
-     * <pre>
-     * StringUtils.mid(null, *, *)    = null
-     * StringUtils.mid(*, *, -ve)     = ""
-     * StringUtils.mid("", 0, *)      = ""
-     * StringUtils.mid("abc", 0, 2)   = "ab"
-     * StringUtils.mid("abc", 0, 4)   = "abc"
-     * StringUtils.mid("abc", 2, 4)   = "c"
-     * StringUtils.mid("abc", 4, 2)   = ""
-     * StringUtils.mid("abc", -2, 2)  = "ab"
-     * </pre>
-     *
-     * @param str  the String to get the characters from, may be null
-     * @param pos  the position to start from, negative treated as zero
-     * @param len  the length of the required String
-     * @return the middle characters, <code>null</code> if null String input
-     */
-    public static String mid(String str, int pos, int len) {
-        if (str == null) {
-            return null;
-        }
-        if (len < 0 || pos > str.length()) {
-            return EMPTY;
-        }
-        if (pos < 0) {
-            pos = 0;
-        }
-        if (str.length() <= (pos + len)) {
-            return str.substring(pos);
-        }
-        return str.substring(pos, pos + len);
-    }
-
-    // SubStringAfter/SubStringBefore
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Gets the substring before the first occurrence of a separator.
-     * The separator is not returned.</p>
-     *
-     * <p>A <code>null</code> string input will return <code>null</code>.
-     * An empty ("") string input will return the empty string.
-     * A <code>null</code> separator will return the input string.</p>
-     *
-     * <p>If nothing is found, the string input is returned.</p>
-     *
-     * <pre>
-     * StringUtils.substringBefore(null, *)      = null
-     * StringUtils.substringBefore("", *)        = ""
-     * StringUtils.substringBefore("abc", "a")   = ""
-     * StringUtils.substringBefore("abcba", "b") = "a"
-     * StringUtils.substringBefore("abc", "c")   = "ab"
-     * StringUtils.substringBefore("abc", "d")   = "abc"
-     * StringUtils.substringBefore("abc", "")    = ""
-     * StringUtils.substringBefore("abc", null)  = "abc"
-     * </pre>
-     *
-     * @param str  the String to get a substring from, may be null
-     * @param separator  the String to search for, may be null
-     * @return the substring before the first occurrence of the separator,
-     *  <code>null</code> if null String input
-     * @since 2.0
-     */
-    public static String substringBefore(String str, String separator) {
-        if (isEmpty(str) || separator == null) {
-            return str;
-        }
-        if (separator.length() == 0) {
-            return EMPTY;
-        }
-        int pos = str.indexOf(separator);
-        if (pos == INDEX_NOT_FOUND) {
-            return str;
-        }
-        return str.substring(0, pos);
-    }
-
-    /**
-     * <p>Gets the substring after the first occurrence of a separator.
-     * The separator is not returned.</p>
-     *
-     * <p>A <code>null</code> string input will return <code>null</code>.
-     * An empty ("") string input will return the empty string.
-     * A <code>null</code> separator will return the empty string if the
-     * input string is not <code>null</code>.</p>
-     *
-     * <p>If nothing is found, the empty string is returned.</p>
-     *
-     * <pre>
-     * StringUtils.substringAfter(null, *)      = null
-     * StringUtils.substringAfter("", *)        = ""
-     * StringUtils.substringAfter(*, null)      = ""
-     * StringUtils.substringAfter("abc", "a")   = "bc"
-     * StringUtils.substringAfter("abcba", "b") = "cba"
-     * StringUtils.substringAfter("abc", "c")   = ""
-     * StringUtils.substringAfter("abc", "d")   = ""
-     * StringUtils.substringAfter("abc", "")    = "abc"
-     * </pre>
-     *
-     * @param str  the String to get a substring from, may be null
-     * @param separator  the String to search for, may be null
-     * @return the substring after the first occurrence of the separator,
-     *  <code>null</code> if null String input
-     * @since 2.0
-     */
-    public static String substringAfter(String str, String separator) {
-        if (isEmpty(str)) {
-            return str;
-        }
-        if (separator == null) {
-            return EMPTY;
-        }
-        int pos = str.indexOf(separator);
-        if (pos == INDEX_NOT_FOUND) {
-            return EMPTY;
-        }
-        return str.substring(pos + separator.length());
-    }
-
-    /**
-     * <p>Gets the substring before the last occurrence of a separator.
-     * The separator is not returned.</p>
-     *
-     * <p>A <code>null</code> string input will return <code>null</code>.
-     * An empty ("") string input will return the empty string.
-     * An empty or <code>null</code> separator will return the input string.</p>
-     *
-     * <p>If nothing is found, the string input is returned.</p>
-     *
-     * <pre>
-     * StringUtils.substringBeforeLast(null, *)      = null
-     * StringUtils.substringBeforeLast("", *)        = ""
-     * StringUtils.substringBeforeLast("abcba", "b") = "abc"
-     * StringUtils.substringBeforeLast("abc", "c")   = "ab"
-     * StringUtils.substringBeforeLast("a", "a")     = ""
-     * StringUtils.substringBeforeLast("a", "z")     = "a"
-     * StringUtils.substringBeforeLast("a", null)    = "a"
-     * StringUtils.substringBeforeLast("a", "")      = "a"
-     * </pre>
-     *
-     * @param str  the String to get a substring from, may be null
-     * @param separator  the String to search for, may be null
-     * @return the substring before the last occurrence of the separator,
-     *  <code>null</code> if null String input
-     * @since 2.0
-     */
-    public static String substringBeforeLast(String str, String separator) {
-        if (isEmpty(str) || isEmpty(separator)) {
-            return str;
-        }
-        int pos = str.lastIndexOf(separator);
-        if (pos == INDEX_NOT_FOUND) {
-            return str;
-        }
-        return str.substring(0, pos);
-    }
-
-    /**
-     * <p>Gets the substring after the last occurrence of a separator.
-     * The separator is not returned.</p>
-     *
-     * <p>A <code>null</code> string input will return <code>null</code>.
-     * An empty ("") string input will return the empty string.
-     * An empty or <code>null</code> separator will return the empty string if
-     * the input string is not <code>null</code>.</p>
-     *
-     * <p>If nothing is found, the empty string is returned.</p>
-     *
-     * <pre>
-     * StringUtils.substringAfterLast(null, *)      = null
-     * StringUtils.substringAfterLast("", *)        = ""
-     * StringUtils.substringAfterLast(*, "")        = ""
-     * StringUtils.substringAfterLast(*, null)      = ""
-     * StringUtils.substringAfterLast("abc", "a")   = "bc"
-     * StringUtils.substringAfterLast("abcba", "b") = "a"
-     * StringUtils.substringAfterLast("abc", "c")   = ""
-     * StringUtils.substringAfterLast("a", "a")     = ""
-     * StringUtils.substringAfterLast("a", "z")     = ""
-     * </pre>
-     *
-     * @param str  the String to get a substring from, may be null
-     * @param separator  the String to search for, may be null
-     * @return the substring after the last occurrence of the separator,
-     *  <code>null</code> if null String input
-     * @since 2.0
-     */
-    public static String substringAfterLast(String str, String separator) {
-        if (isEmpty(str)) {
-            return str;
-        }
-        if (isEmpty(separator)) {
-            return EMPTY;
-        }
-        int pos = str.lastIndexOf(separator);
-        if (pos == INDEX_NOT_FOUND || pos == (str.length() - separator.length())) {
-            return EMPTY;
-        }
-        return str.substring(pos + separator.length());
-    }
-
-    // Substring between
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Gets the String that is nested in between two instances of the
-     * same String.</p>
-     *
-     * <p>A <code>null</code> input String returns <code>null</code>.
-     * A <code>null</code> tag returns <code>null</code>.</p>
-     *
-     * <pre>
-     * StringUtils.substringBetween(null, *)            = null
-     * StringUtils.substringBetween("", "")             = ""
-     * StringUtils.substringBetween("", "tag")          = null
-     * StringUtils.substringBetween("tagabctag", null)  = null
-     * StringUtils.substringBetween("tagabctag", "")    = ""
-     * StringUtils.substringBetween("tagabctag", "tag") = "abc"
-     * </pre>
-     *
-     * @param str  the String containing the substring, may be null
-     * @param tag  the String before and after the substring, may be null
-     * @return the substring, <code>null</code> if no match
-     * @since 2.0
-     */
-    public static String substringBetween(String str, String tag) {
-        return substringBetween(str, tag, tag);
-    }
-
-    /**
-     * <p>Gets the String that is nested in between two Strings.
-     * Only the first match is returned.</p>
-     *
-     * <p>A <code>null</code> input String returns <code>null</code>.
-     * A <code>null</code> open/close returns <code>null</code> (no match).
-     * An empty ("") open and close returns an empty string.</p>
-     *
-     * <pre>
-     * StringUtils.substringBetween("wx[b]yz", "[", "]") = "b"
-     * StringUtils.substringBetween(null, *, *)          = null
-     * StringUtils.substringBetween(*, null, *)          = null
-     * StringUtils.substringBetween(*, *, null)          = null
-     * StringUtils.substringBetween("", "", "")          = ""
-     * StringUtils.substringBetween("", "", "]")         = null
-     * StringUtils.substringBetween("", "[", "]")        = null
-     * StringUtils.substringBetween("yabcz", "", "")     = ""
-     * StringUtils.substringBetween("yabcz", "y", "z")   = "abc"
-     * StringUtils.substringBetween("yabczyabcz", "y", "z")   = "abc"
-     * </pre>
-     *
-     * @param str  the String containing the substring, may be null
-     * @param open  the String before the substring, may be null
-     * @param close  the String after the substring, may be null
-     * @return the substring, <code>null</code> if no match
-     * @since 2.0
-     */
-    public static String substringBetween(String str, String open, String close) {
-        if (str == null || open == null || close == null) {
-            return null;
-        }
-        int start = str.indexOf(open);
-        if (start != INDEX_NOT_FOUND) {
-            int end = str.indexOf(close, start + open.length());
-            if (end != INDEX_NOT_FOUND) {
-                return str.substring(start + open.length(), end);
-            }
-        }
-        return null;
-    }
-
-}

+ 30 - 5
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java

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

Some files were not shown because too many files changed in this diff