前后端处理验证码kaptcha

本文介绍了如何在Spring Boot后端利用Kaptcha生成随机验证码,并通过UUID作为key存储在Redis中,同时发送Base64编码的验证码图片到前端。前端在登录时,将接收到的验证码和key一并发送回后端进行校验。此外,还展示了CaptchaFilter用于验证码的校验。整个流程确保了验证码的安全性和有效性。
摘要由CSDN通过智能技术生成

方案

  1. 前端在登录页面时,请求验证码的信息;
  2. 后端可以利用UUID生成随机数key和验证码code存储在redis中,设置好倒计时间;
  3. 后端再把随机数key和含验证码code的base64的图片传给前端;
  4. 前端在登录接口时,把用户名/密码/key/验证码code一起传给后端;
  5. 后端对前端传的key和code和redis中存储的进行比对,并把验证码失败或者成功结果传给前端;
    在这里插入图片描述

代码实现

后端

kaptcha谷歌验证码工具依赖

 <!-- 验证码 -->
 <dependency>
     <groupId>com.github.penggle</groupId>
     <artifactId>kaptcha</artifactId>
     <version>2.3.2</version>
 </dependency>

config文件配置图片验证码的生成规则;具体配置参数可查看此链接

@Configuration
public class KaptchaConfig {

    @Bean
    DefaultKaptcha producer() {
        Properties properties = new Properties();
        properties.put("kaptcha.border", "no");
        properties.put("kaptcha.textproducer.font.color", "black");
        properties.put("kaptcha.textproducer.char.space", "4");
        properties.put("kaptcha.image.height", "40");
        properties.put("kaptcha.image.width", "120");
        properties.put("kaptcha.textproducer.font.size", "30");

        Config config = new Config(properties);
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        defaultKaptcha.setConfig(config);

        return defaultKaptcha;
    }
}

提供验证码的接口;别忘了在安全访问的配置中取消/captcha接口的token验证;

    @GetMapping("/captcha")
    public ResultJson captcha() throws IOException {

        String key = UUID.randomUUID().toString();
        String code = producer.createText();

        BufferedImage image = producer.createImage(code);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ImageIO.write(image, "jpg", outputStream);

        BASE64Encoder encoder = new BASE64Encoder();
        String str = "data:image/jpeg;base64,";

        String base64Img = str + encoder.encode(outputStream.toByteArray());

        //存储key和code到redis中
        boolean flag = redisUtil.hset(Const.CAPTCHA_KEY, key, code, 120000); //时间是毫秒

		//传输base64验证码图片和token
        Map<String, Object> map = new HashMap<>();
        map.put("token", key);
        map.put("captchaImg", base64Img);

        return ResultJson.ok().data(map);
    }

CaptchaFilter 图片验证码校验过滤器

/**
 * 图片验证码校验过滤器,在登录过滤器前
 */
@Slf4j
@Component
public class CaptchaFilter extends OncePerRequestFilter {
   private final String loginUrl = "/login";
   @Autowired
   RedisUtil redisUtil;
   @Autowired
   LoginFailureHandler loginFailureHandler;
   @Override
   protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
         throws ServletException, IOException {
      String url = request.getRequestURI();
      if (loginUrl.equals(url) && request.getMethod().equals("POST")) {
         log.info("获取到login链接,正在校验验证码 -- " + url);
         try {
            validate(request);
         } catch (CaptchaException e) {
            log.info(e.getMessage());
            // 交给登录失败处理器处理
            loginFailureHandler.onAuthenticationFailure(request, response, e);
         }
      }
      filterChain.doFilter(request, response);
   }
   private void validate(HttpServletRequest request) {
      String code = request.getParameter("code");
      String token = request.getParameter("token");
      if (StringUtils.isBlank(code) || StringUtils.isBlank(token)) {
         throw new CaptchaException("验证码不能为空");
      }
      if(!code.equals(redisUtil.hget(Const.captcha_KEY, token))) {
         throw new CaptchaException("验证码不正确");
      }
      // 一次性使用
      redisUtil.hdel(Const.captcha_KEY, token);
   }
}
前端

请求/captcha接口

    getCaptcha() {
      this.$axios.get("/captcha").then((res) => {
        let data = res.data.data;
        this.loginForm.token = data.token;
        this.captchaImg = data.captchaImg;
      });

显示验证码,点击可切换验证码

            <el-form-item label="验证码" prop="code">
              <el-input
                v-model="loginForm.code"
                prefix-icon="el-icon-key"
              ></el-input>
              <el-image
                :src="captchaImg"
                class="captchaImg"
                @click="getCaptcha"
              ></el-image>
            </el-form-item>

在这里插入图片描述

请求登录接口方法

  data() {
    return {
      loginForm: {
        username: null,
        password: null,
        code: null,//验证码
        token: null,//验证码的token
      },
    };
  },
 methods: {
    loginSubmit(formName) {
      this.$refs[formName].validate((valid) => {
        //表单校验
        if (valid) {
          
          axios
            .post("/login", this.$qs.stringify(this.loginForm), {
              headers: { "Content-Type": "application/x-www-form-urlencoded" },
            })
            .then((response) => {
              console.log(response.data);
              var message = response.data.message;
              var success = response.data.success;
              var token = response.data.data.token;
              var refreshToken = response.data.data.refreshToken;
              console.log(message);

              var messageType = null;
              console.log("success的值:", success);
              console.log(typeof success);
              if (success === true) {
                messageType = "success";
              } else {
                messageType = "error";
                this.getCaptcha();//重新请求验证码接口
              }
              console.log(messageType);
              this.$message({
                //登录提示信息
                message: message,
                type: messageType, 
              });

              if (token) {
                //token有值
                localStorage.setItem("token", token); //将token存入本地
                localStorage.setItem("refreshToken", refreshToken); //将token存入本地
                this.$router.push({name:"Index"}); //跳转到首页
              }
            })
            .catch(function(error) {
              // 请求失败处理
              console.log(error);
            });
          console.log("login submit!");
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    },
}

参考

https://www.zhuawaba.com/post/19#%E7%94%9F%E6%88%90%E9%AA%8C%E8%AF%81%E7%A0%81

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值