zhubaomin
2024-09-11 da6191fafa9aa07a1f4f56487d23fbc703a935b9
2024-09-11 朱宝民 新加获取图片验证码接口,完善单点登录接口
4个文件已修改
10个文件已添加
834 ■■■■■ 已修改文件
pipIrr-platform/pipIrr-global/pom.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/config/KaptchaConfig.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/config/KaptchaMathOneTextCreator.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/config/KaptchaMathTwoTextCreator.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoBa/BaCaptchaMapper.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoBa/BaCaptcha.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/util/CaptchaUtil.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/mapper/BaCaptchaMapper.xml 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sso/src/main/java/com/dy/sso/busi/LoginVo.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sso/src/main/java/com/dy/sso/busi/SsoCtrl.java 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sso/src/main/java/com/dy/sso/busi/SsoSv.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/captcha/CaptchaCtrl.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/captcha/CaptchaSV.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/captcha/dto/CaptchaDomain.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/pom.xml
@@ -158,6 +158,13 @@
            <artifactId>commons-codec</artifactId>
            <version>1.15</version>
        </dependency>
        <!-- 图片验证码 -->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>
    </dependencies>
    <build>
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/config/KaptchaConfig.java
New file
@@ -0,0 +1,128 @@
package com.dy.pipIrrGlobal.config;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
/**
 * @author ZhuBaoMin
 * @date 2024-09-10 11:17
 * @LastEditTime 2024-09-10 11:17
 * @Description Google验证码配置类,配置三种类型的验证码生成器:简单文本验证码、一位数加减乘除验证码、两位数加减乘除验证码
 */
@Configuration
public class KaptchaConfig {
    private static KaptchaConfig instance;
    /**
     * 验证码配置默认配置
     * @return
     */
    @Bean(name = "captchaProducer")
    public DefaultKaptcha getKaptchaBean() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 是否有边框。默认true,可选:yes,no
        properties.setProperty("kaptcha.border", "yes");
        //设置图片边框颜色
        properties.setProperty("kaptcha.border.color", "green");
        // 验证码文本字符颜色。默认Color.BLACK
        properties.setProperty("kaptcha.textproducer.font.color", "black");
        // 文字间隔
        properties.put("kaptcha.textproducer.char.space", "10");
        // 验证码图片宽度。默认200
        properties.setProperty("kaptcha.image.width", "160");
        // 验证码图片高度。默认50
        properties.setProperty("Kkaptcha.image.height", "60");
        // 验证码文本字符大小。默认40
        properties.setProperty("kaptcha.textproducer.font.size", "38");
        // KAPTCHA_SESSION_KEY
        properties.setProperty("Kkaptcha.session.key", "kaptchaCode");
        // 验证码文本字符长度。默认5
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        // 验证码文本字体样式。默认:new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
        properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier");
        // 干扰实现类
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
        // 图片样式。
        // 水纹:com.google.code.kaptcha.impl.WaterRipple
        // 鱼眼:com.google.code.kaptcha.impl.FishEyeGimpy
        // 阴影:com.google.code.kaptcha.impl.ShadowGimpy
        properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
    /**
     * 验证码数学题类配置(一位数加减乘除)
     * @return 配置信息
     */
    @Bean(name = "captchaProducerMathOne")
    public DefaultKaptcha getKaptchaBeanMathOne() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = commonConfig("com.dy.pipIrrGlobal.config.KaptchaMathOneTextCreator");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
    /**
     * 验证码数学题类配置(两位数的加减乘除)
     * @return 配置信息
     */
    @Bean(name = "captchaProducerMathTwo")
    public DefaultKaptcha getKaptchaBeanMathTwo() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = commonConfig("com.dy.pipIrrGlobal.config.KaptchaMathTwoTextCreator");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
    /**
     * 算式运算配置的公共配置类
     * @param textImpl 验证码文本生成器
     * @return 配置类
     */
    protected static Properties commonConfig(String textImpl) {
        Properties properties = new Properties();
        // 是否有边框。默认为true,可设置:yes,no
        properties.setProperty("kaptcha.border", "yes");
        // 边框颜色。默认:Color.BLACK
        properties.setProperty("kaptcha.border.color", "105,179,90");
        // 验证码文本字符颜色。默认:Color.BLACK
        properties.setProperty("kaptcha.textproducer.font.color", "blue");
        // 验证码图片宽度。默认:200
        properties.setProperty("kaptcha.image.width", "160");
        // 验证码图片高度。默认:50
        properties.setProperty("kaptcha.image.height", "60");
        // 验证码文本字符大小。默认:40
        properties.setProperty("kaptcha.textproducer.font.size", "35");
        // KAPTCHA_SESSION_KEY
        properties.setProperty("kaptcha.session.key", "kaptchaCodeMath");
        // 验证码文本生成器
        properties.setProperty("kaptcha.textproducer.impl", textImpl);
        // 验证码文本字符间距。默认:2
        properties.setProperty("kaptcha.textproducer.char.space", "6");
        // 验证码文本字符长度。默认:5
        properties.setProperty("kaptcha.textproducer.char.length", "6");
        // 验证码文本字体样式。默认:new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
        properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier");
        // 验证码噪点颜色。默认:Color.BLACK
        properties.setProperty("kaptcha.noise.color", "white");
        // 干扰实现类
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
        // 图片样式。
        // 水纹:com.google.code.kaptcha.impl.WaterRipple
        // 鱼眼:com.google.code.kaptcha.impl.FishEyeGimpy
        // 阴影:com.google.code.kaptcha.impl.ShadowGimpy
        properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
        // 返回生成的配置类
        return properties;
    }
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/config/KaptchaMathOneTextCreator.java
New file
@@ -0,0 +1,25 @@
package com.dy.pipIrrGlobal.config;
import com.dy.pipIrrGlobal.util.CaptchaUtil;
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
import java.security.SecureRandom;
import java.util.Map;
import java.util.Random;
/**
 * @author ZhuBaoMin
 * @date 2024-09-10 10:38
 * @LastEditTime 2024-09-10 10:38
 * @Description 验证码随机文本生成器:一位数的加减乘除
 */
public class KaptchaMathOneTextCreator extends DefaultTextCreator {
    @Override
    public String getText() {
        Random random = new SecureRandom();
        // 生成两个随机数,随机数范围:[0,10),并返回结果
        Map<String, String> result = CaptchaUtil.mathTextCreator(random.nextInt(10), random.nextInt(10));
        return result.get("resultString");
    }
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/config/KaptchaMathTwoTextCreator.java
New file
@@ -0,0 +1,26 @@
package com.dy.pipIrrGlobal.config;
import com.dy.pipIrrGlobal.util.CaptchaUtil;
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
import java.security.SecureRandom;
import java.util.Map;
import java.util.Random;
/**
 * @author ZhuBaoMin
 * @date 2024-09-10 10:39
 * @LastEditTime 2024-09-10 10:39
 * @Description 验证码随机文本生成器:两位数的加减乘除
 */
public class KaptchaMathTwoTextCreator extends DefaultTextCreator {
    @Override
    public String getText() {
        Random random = new SecureRandom();
        // 保存计算结果
        Map<String, String> result = CaptchaUtil.mathTextCreator(random.nextInt(100), random.nextInt(100));
        // 生成两个随机数,随机数范围:[0,100),并返回结果
        return result.get("resultString");
    }
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoBa/BaCaptchaMapper.java
New file
@@ -0,0 +1,36 @@
package com.dy.pipIrrGlobal.daoBa;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dy.pipIrrGlobal.pojoBa.BaCaptcha;
import org.apache.ibatis.annotations.Mapper;
import java.util.Map;
/**
 * @author ZhuBaoMin
 * @date 2024-09-11 10:58
 * @LastEditTime 2024-09-11 10:58
 * @Description
 */
@Mapper
public interface BaCaptchaMapper extends BaseMapper {
    int deleteByPrimaryKey(Long id);
    int insert(BaCaptcha record);
    int insertSelective(BaCaptcha record);
    BaCaptcha selectByPrimaryKey(Long id);
    int updateByPrimaryKeySelective(BaCaptcha record);
    int updateByPrimaryKey(BaCaptcha record);
    /**
     * 依据验证码token获取验证字符
     * @param token
     * @return
     */
    Map getCodeByToken(String token);
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoBa/BaCaptcha.java
New file
@@ -0,0 +1,56 @@
package com.dy.pipIrrGlobal.pojoBa;
/**
 * @author ZhuBaoMin
 * @date 2024-09-11 10:58
 * @LastEditTime 2024-09-11 10:58
 * @Description
 */
import com.alibaba.fastjson2.annotation.JSONField;
import com.alibaba.fastjson2.writer.ObjectWriterImplToString;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.dy.common.po.BaseEntity;
import jakarta.validation.constraints.NotBlank;
import lombok.*;
/**
 * 图形验证记录表
 */
@TableName(value = "ba_captcha", autoResultMap = true)
@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class BaCaptcha implements BaseEntity {
    public static final long serialVersionUID = 202409111103001L;
    /**
    * 主键
    */
    @JSONField(serializeUsing = ObjectWriterImplToString.class)
    @TableId(type = IdType.INPUT)
    private Long id;
    /**
    * 验证码token
    */
    @NotBlank(message = "验证码token不能为空")
    private String token;
    /**
    * 验证字符
    */
    @NotBlank(message = "验证字符不能为空")
    private String code;
    /**
    * 过期时间
    */
    @JSONField(serializeUsing = ObjectWriterImplToString.class)
    private Long expiration;
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/util/CaptchaUtil.java
New file
@@ -0,0 +1,67 @@
package com.dy.pipIrrGlobal.util;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
 * @author ZhuBaoMin
 * @date 2024-09-10 10:32
 * @LastEditTime 2024-09-10 10:32
 * @Description 自定义验证码工具类
 */
public class CaptchaUtil {
    /**
     * 生成数学文本算式,例如1+1=?
     * @param a 数字
     * @param b 数字
     * @return  字符
     */
    public static Map<String, String> mathTextCreator(int a, int b) {
        Random random = new SecureRandom();
        // 生成随机操作,操作范围:[0,4),分别表示: +  -  *  /
        int op = random.nextInt(4);
        // 定义计算的结果
        Integer result = 0;
        // 定义构建的算式字符串
        StringBuilder resultString = new StringBuilder();
        // 运算符:-
        if (1 == op) {
            if (a >= b) {
                result = a - b;
                resultString.append(a).append("-").append(b).append("=?@").append(result);
            } else {
                result = b - a;
                resultString.append(b).append("-").append(a).append("=?@").append(result);
            }
        }
        // 运算符:*
        else if (2 == op) {
            result = a * b;
            resultString.append(a).append("*").append(b).append("=?@").append(result);
        }
        // 运算符:/
        else if (3 == op) {
            if (a != 0 && b % a == 0) {
                result = b / a;
                resultString.append(b).append("/").append(a).append("=?@").append(result);
            } else if (b != 0 && a % b == 0) {
                result = a / b;
                resultString.append(a).append("/").append(b).append("=?@").append(result);
            } else {
                return mathTextCreator(a, b);
            }
        }
        // 运算符:+
        else {
            result = b + a;
            resultString.append(a).append("+").append(b).append("=?@").append(result);
        }
        Map<String, String> ret = new HashMap<String, String>();
        ret.put("resultCode", result.toString());
        ret.put("resultString", resultString.toString());
        return ret;
    }
}
pipIrr-platform/pipIrr-global/src/main/resources/mapper/BaCaptchaMapper.xml
New file
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dy.pipIrrGlobal.daoBa.BaCaptchaMapper">
  <resultMap id="BaseResultMap" type="com.dy.pipIrrGlobal.pojoBa.BaCaptcha">
    <!--@mbg.generated-->
    <!--@Table ba_captcha-->
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="token" jdbcType="VARCHAR" property="token" />
    <result column="code" jdbcType="VARCHAR" property="code" />
    <result column="expiration" jdbcType="BIGINT" property="expiration" />
  </resultMap>
  <sql id="Base_Column_List">
    <!--@mbg.generated-->
    id, token, code, expiration
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    <!--@mbg.generated-->
    select
    <include refid="Base_Column_List" />
    from ba_captcha
    where id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    <!--@mbg.generated-->
    delete from ba_captcha
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.dy.pipIrrGlobal.pojoBa.BaCaptcha">
    <!--@mbg.generated-->
    insert into ba_captcha (id, token, code,
      expiration)
    values (#{id,jdbcType=BIGINT}, #{token,jdbcType=VARCHAR}, #{code,jdbcType=VARCHAR},
      #{expiration,jdbcType=BIGINT})
  </insert>
  <insert id="insertSelective" parameterType="com.dy.pipIrrGlobal.pojoBa.BaCaptcha">
    <!--@mbg.generated-->
    insert into ba_captcha
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="token != null">
        token,
      </if>
      <if test="code != null">
        code,
      </if>
      <if test="expiration != null">
        expiration,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=BIGINT},
      </if>
      <if test="token != null">
        #{token,jdbcType=VARCHAR},
      </if>
      <if test="code != null">
        #{code,jdbcType=VARCHAR},
      </if>
      <if test="expiration != null">
        #{expiration,jdbcType=BIGINT},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.dy.pipIrrGlobal.pojoBa.BaCaptcha">
    <!--@mbg.generated-->
    update ba_captcha
    <set>
      <if test="token != null">
        token = #{token,jdbcType=VARCHAR},
      </if>
      <if test="code != null">
        code = #{code,jdbcType=VARCHAR},
      </if>
      <if test="expiration != null">
        expiration = #{expiration,jdbcType=BIGINT},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.dy.pipIrrGlobal.pojoBa.BaCaptcha">
    <!--@mbg.generated-->
    update ba_captcha
    set token = #{token,jdbcType=VARCHAR},
      code = #{code,jdbcType=VARCHAR},
      expiration = #{expiration,jdbcType=BIGINT}
    where id = #{id,jdbcType=BIGINT}
  </update>
  <!--依据验证码token获取验证字符-->
  <select id="getCodeByToken" resultType="java.util.Map">
    SELECT code, expiration FROM ba_captcha WHERE token = #{token}
  </select>
</mapper>
pipIrr-platform/pipIrr-web/pipIrr-web-sso/src/main/java/com/dy/sso/busi/LoginVo.java
@@ -33,5 +33,15 @@
    @NotEmpty(message = "组织单位不能为空") //不能为空也不能为null
    @Length(message = "组织单位标签必须大于{min}位", min = 2)
    public String orgTag ;
    /**
     * 图形验证码token,即session的key
     */
    private String token;
    /**
     * 图形验证码结果
     */
    private String code;
}
pipIrr-platform/pipIrr-web/pipIrr-web-sso/src/main/java/com/dy/sso/busi/SsoCtrl.java
@@ -25,9 +25,7 @@
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.*;
/**
 * 注解Tag 在API中显示: Tag 注解, 给整个接口起了个名字与描述"
@@ -45,6 +43,7 @@
    //在属性上注解@Autowired时,会警告 Field injection is not recommended(不再推荐使用字段注入)
    private SsoSv sv ;
    //private KaptchaConfig kaptchaConfig;
    //@Autowired
    //private CacheManager cacheManager ;
@@ -53,6 +52,10 @@
        this.sv = sv ;
    }
    //@Autowired
    //public void setKaptchaConfig(KaptchaConfig kaptchaConfig) {
    //    this.kaptchaConfig = kaptchaConfig;
    //}
    /**
     * 客户端请求得到所有组织机构
@@ -98,7 +101,7 @@
            )
    })
    @PostMapping(path = "loginJson", consumes = MediaType.APPLICATION_JSON_VALUE)
    public BaseResponse<UserVo> loginJson(@RequestBody @Parameter(description = "登录json数据", required = true) @Valid LoginVo vo,  @Parameter(hidden = true) BindingResult bindingResult) {
    public BaseResponse<UserVo> loginJson(HttpServletRequest request, @RequestBody @Parameter(description = "登录json数据", required = true) @Valid LoginVo vo,  @Parameter(hidden = true) BindingResult bindingResult) {
        if(bindingResult != null && bindingResult.hasErrors()){
            return BaseResponseUtils.buildErrorMsg(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
@@ -113,6 +116,32 @@
        //把组织单位标签作为数据源名称
        DataSourceContext.set(vo.orgTag);
        String token = Optional.ofNullable(vo.getToken()).orElse("");
        String code = Optional.ofNullable(vo.getCode()).orElse("");
        if(token.length() > 0 && code.length() > 0) {
            // 从session中获取验证码
            //HttpSession session = (HttpSession) request.getSession();
            //String localCode = session.getAttribute(token).toString();
            Map map = sv.getCodeByToken(token);
            Long expiration = Long.parseLong(map.get("expiration").toString());
            Long currentTimestamp = System.currentTimeMillis();
            if(currentTimestamp > expiration) {
                return BaseResponseUtils.buildErrorMsg("验证码已超时");
            }
            // 从数据库获取验证码
            String localCode = map.get("code").toString();
            if(!code.equals(localCode)) {
                return BaseResponseUtils.buildErrorMsg("验证码错误");
            }
        }
        //得到所有用户账号
        List<String> phones = sv.getPhones();
        if(!phones.contains(vo.phone)){
pipIrr-platform/pipIrr-web/pipIrr-web-sso/src/main/java/com/dy/sso/busi/SsoSv.java
@@ -1,6 +1,7 @@
package com.dy.sso.busi;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dy.pipIrrGlobal.daoBa.BaCaptchaMapper;
import com.dy.pipIrrGlobal.daoBa.BaUserMapper;
import com.dy.pipIrrGlobal.pojoBa.BaUser;
import lombok.extern.slf4j.Slf4j;
@@ -11,11 +12,11 @@
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
public class SsoSv {
    private BaUserMapper baUserMapper;
    @Autowired
@@ -23,6 +24,8 @@
        this.baUserMapper = baUserMapper ;
    }
    @Autowired
    private BaCaptchaMapper baCaptchaMapper;
    /**
     * 需要BaUserMapper.xml
@@ -76,4 +79,13 @@
    public List<String> getPhones(){
        return baUserMapper.getPhones() ;
    }
    /**
     * 依据验证码token获取验证字符
     * @param token
     * @return
     */
    public Map getCodeByToken(String token) {
        return baCaptchaMapper.getCodeByToken(token);
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/captcha/CaptchaCtrl.java
New file
@@ -0,0 +1,129 @@
package com.dy.pipIrrWechat.captcha;
import com.dy.pipIrrGlobal.pojoBa.BaCaptcha;
import com.dy.pipIrrWechat.captcha.dto.CaptchaDomain;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.util.Calendar;
/**
 * @author ZhuBaoMin
 * @date 2024-09-10 11:40
 * @LastEditTime 2024-09-10 11:40
 * @Description 图片验证码
 */
@Slf4j
@RestController
@RequestMapping(path="captcha")
public class CaptchaCtrl {
    @Autowired
    private CaptchaSV captchaSV;
    /**
     * 获取验证码
     * @param type,图片类型:char-文本,math-一位数算式,math2-两位数算式
     * @return
     */
    @GetMapping("/get")
    @ResponseBody
    public CaptchaDomain getCaptcha(HttpServletRequest request, @RequestParam(value = "type", required = false, defaultValue = "char") String type) {
        // 生成验证码实体
        CaptchaDomain captchaDomain = captchaSV.createGoogleCaptcha(type);
        if (null != captchaDomain) {
            // 验证码保存至redis
            // redisUtils.set(captchaDomain.getToken(), captchaDomain.getCode(), 300L);
            // 验证码保存至session
            //HttpSession session = (HttpSession) request.getSession();
            //session.setAttribute(captchaDomain.getToken(), captchaDomain.getCode());
            /**
             * 验证保存到数据库
             * 获取当前时间戳并延后3分钟
             */
            Long timestamp = System.currentTimeMillis();
            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(timestamp);
            calendar.add(Calendar.SECOND, 180);
            BaCaptcha baCaptcha = new BaCaptcha();
            baCaptcha.setToken(captchaDomain.getToken());
            baCaptcha.setCode(captchaDomain.getCode());
            baCaptcha.setExpiration(calendar.getTimeInMillis());
            Long rec = captchaSV.addCaptcha(baCaptcha);
            System.out.println("token: " + captchaDomain.getToken() + "; code: " + captchaDomain.getCode());
            // 无用信息设空
            captchaDomain.setText(null);
            captchaDomain.setCode(null);
            // 返回前端信息
            return captchaDomain;
        } else {
            return null;
        }
    }
    /**
     * 直接输出图片
     * @param type,图片类型:char-文本,math-一位数算式,math2-两位数算式
     */
    @GetMapping("/get/image")
    public void getCaptchaImage(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "type", required = false, defaultValue = "char") String type) {
        CaptchaDomain captchaDomain = null;
        // 生成谷歌验证码实体
        captchaDomain = captchaSV.createGoogleCaptcha(type);
        // 验证码保存至redis
        // redisUtils.set(captchaDomain.getToken(), captchaDomain.getCode(), 300L);
        // 验证码保存至session
        //HttpSession session = (HttpSession) request.getSession();
        //session.setAttribute(captchaDomain.getToken(), captchaDomain.getCode());
        /**
         * 验证保存到数据库
         * 获取当前时间戳并延后3分钟
         */
        Long timestamp = System.currentTimeMillis();
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(timestamp);
        calendar.add(Calendar.SECOND, 180);
        BaCaptcha baCaptcha = new BaCaptcha();
        baCaptcha.setToken(captchaDomain.getToken());
        baCaptcha.setCode(captchaDomain.getCode());
        baCaptcha.setExpiration(calendar.getTimeInMillis());
        Long rec = captchaSV.addCaptcha(baCaptcha);
        System.out.println("token: " + captchaDomain.getToken() + "; code: " + captchaDomain.getCode());
        // 以文件流的形式,输出验证码图片
        ServletOutputStream out = null;
        try {
            response.setContentType("image/jpeg");
            out = response.getOutputStream();
            ImageIO.write(captchaDomain.getImage(), "jpg", out);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/captcha/CaptchaSV.java
New file
@@ -0,0 +1,104 @@
package com.dy.pipIrrWechat.captcha;
import com.dy.pipIrrGlobal.daoBa.BaCaptchaMapper;
import com.dy.pipIrrGlobal.pojoBa.BaCaptcha;
import com.dy.pipIrrWechat.captcha.dto.CaptchaDomain;
import com.google.code.kaptcha.Producer;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
import java.io.ByteArrayOutputStream;
import java.util.Base64;
import java.util.UUID;
/**
 * @author ZhuBaoMin
 * @date 2024-09-10 11:40
 * @LastEditTime 2024-09-10 11:40
 * @Description
 */
@Slf4j
@Service
public class CaptchaSV {
    @Autowired
    private BaCaptchaMapper baCaptchaMapper;
    @Resource(name = "captchaProducer")
    private Producer captchaProducer;
    @Resource(name = "captchaProducerMathOne")
    private Producer captchaProducerMathOne;
    @Resource(name = "captchaProducerMathTwo")
    private Producer captchaProducerMathTwo;
    private static final String TYPE_CHAR = "char";
    private static final String TYPE_MATH_ONE = "math";
    private static final String TYPE_MATH_TWO = "math2";
    /**
     * Kaptcha生成验证码实体
     * @param type,char - 字符(缺省) | math - 一位数算式 | math2 - 两位数算式
     * @return
     */
    public CaptchaDomain createGoogleCaptcha(String type) {
        // 定义验证码实体
        CaptchaDomain captchaDomain = new CaptchaDomain();
        // 一位数加减乘除
        if (TYPE_MATH_ONE.equals(type)) {
            // 生成文本
            String producerText = captchaProducerMathOne.createText();
            // 设置验证码字符
            captchaDomain.setText(producerText.substring(0, producerText.indexOf("@")));
            // 设置验证码答案码
            captchaDomain.setCode(producerText.substring(producerText.indexOf("@") + 1));
            // 设置验证码图片
            captchaDomain.setImage(captchaProducerMathOne.createImage(captchaDomain.getText()));
        }
        // 两位数加减乘除
        else if (TYPE_MATH_TWO.equals(type)) {
            String producerText = captchaProducerMathTwo.createText();
            captchaDomain.setText(producerText.substring(0, producerText.indexOf("@")));
            captchaDomain.setCode(producerText.substring(producerText.indexOf("@") + 1));
            captchaDomain.setImage(captchaProducerMathTwo.createImage(captchaDomain.getText()));
        }
        // 缺省情况:字符
        else {
            captchaDomain.setText(captchaProducer.createText());
            captchaDomain.setCode(captchaDomain.getText());
            captchaDomain.setImage(captchaProducer.createImage(captchaDomain.getText()));
        }
        // 生成base64
        try {
            // 定义字节数组输出流
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            // 将图像以 jpg 的形式,写到字节数组输出流中
            ImageIO.write(captchaDomain.getImage(), "jpg", outputStream);
            // 写入base64格式
            captchaDomain.setBase64("data:image/jpg;base64," + Base64.getEncoder().encodeToString(outputStream.toByteArray()));
            // 写入唯一Token
            captchaDomain.setToken(UUID.randomUUID().toString());
            // 返回结果
            return captchaDomain;
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return null;
        }
    }
    /**
     * 添加图片验证记录
     * @param po
     * @return
     */
    public Long addCaptcha(BaCaptcha po) {
        baCaptchaMapper.insert(po);
        return po.getId();
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/captcha/dto/CaptchaDomain.java
New file
@@ -0,0 +1,99 @@
package com.dy.pipIrrWechat.captcha.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.awt.image.BufferedImage;
import java.io.Serializable;
/**
 * @author ZhuBaoMin
 * @date 2024-09-10 11:36
 * @LastEditTime 2024-09-10 11:36
 * @Description 第三方验证码实体类
 */
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CaptchaDomain implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 验证码的Token
     */
    private String token;
    /**
     * 验证码的字符。返回的JSON,禁止返回给前端。
     */
    @JsonIgnore
    private String text;
    /**
     * 验证码的验证字符。比如算式的结果等。
     */
    @JsonIgnore
    private String code;
    /**
     * 验证码缓冲图像
     */
    @JsonIgnore
    private BufferedImage image;
    /**
     * 验证码图片的Base64字符串
     */
    private String base64;
    public String getToken() {
        return token;
    }
    public void setToken(String token) {
        this.token = token;
    }
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public BufferedImage getImage() {
        return image;
    }
    public void setImage(BufferedImage image) {
        this.image = image;
    }
    public String getBase64() {
        return base64;
    }
    public void setBase64(String base64) {
        this.base64 = base64;
    }
    @Override
    public String toString() {
        return "CaptchaDomain{" +
                "token='" + token + '\'' +
                ", text='" + text + '\'' +
                ", code='" + code + '\'' +
                ", image=" + image +
                ", base64='" + base64 + '\'' +
                '}';
    }
}