旗下品牌:
石家庄网站开发 石家庄网站开发公司

资讯动态

察而思、思而行、行而后语、知行合一

前端实现图形验证码:生成、展示与登录表单校验

发布时间:2026-09-04 热度:

前言

在 Web 登录场景中,验证码是常见的防护手段,可以有效防止机器批量暴力破解、脚本恶意提交表单。很多小伙伴会疑惑:验证码是不是只能后端生成?其实前端也可以实现简易验证码,在浏览器内生成随机验证码图片,登录提交前先在前端校验验证码,校验通过才允许提交表单。

⚠️重要提醒:前端验证码仅做第一层体验拦截,不能作为安全防线。前端代码可以被绕过,正式项目必须在服务端再次校验验证码,否则会被脚本直接跳过前端逻辑发起请求。本文演示纯浏览器端实现,用于学习交互逻辑。

实现思路

  1. 使用 <canvas> 画布动态绘制验证码图片;

  2. 随机生成 4 位验证码字符(数字 + 大小写字母);

  3. 画布增加干扰线、干扰点、随机字体颜色、旋转角度,增加识别门槛;

  4. 点击验证码图片可以刷新生成新验证码;

  5. 登录表单提交事件拦截,获取用户输入的验证码,和内存保存的正确验证码做对比;

  6. 验证码不一致:提示错误,阻止表单提交;验证码一致:放行,提交表单。

完整代码示例


完整代码示例

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>前端验证码登录示例</title>   <style>     .login-box {       width: 360px;       margin: 80px auto;       padding: 24px;       border: 1px solid #eee;       border-radius: 8px;     }     .item {       margin-bottom:16px;     }     label{       display: block;       margin-bottom:4px;     }     input{       width:100%;       box-sizing: border-box;       padding:8px;       border:1px solid #ccc;       border-radius:4px;     }     .code-wrap{       display:flex;       gap:10px;     }     .code-wrap input{       flex:1;     }     canvas{       cursor:pointer;       border:1px solid #ccc;       border-radius:4px;     }     button{       width:100%;       padding:10px;       background:#2b7bdd;       color:#fff;       border:none;       border-radius:4px;       cursor:pointer;     }     .tip{       color:red;       font-size:13px;       height:18px;     }   </style> </head> <body> <div>   <form id="loginForm">     <div>       <label>账号</label>       <input type="text" name="username" placeholder="请输入账号">     </div>     <div>       <label>密码</label>       <input type="password" name="password" placeholder="请输入密码">     </div>     <div>       <label>验证码</label>       <div>         <input type="text" id="codeInput" placeholder="输入验证码">         <canvas id="codeCanvas" width="120" height="40"></canvas>       </div>     </div>     <div id="errorTip"></div>     <button type="submit">登录</button>   </form> </div> <script>   // 全局保存正确的验证码   let validCode = '';   const canvas = document.getElementById('codeCanvas');   const ctx = canvas.getContext('2d');   const codeInput = document.getElementById('codeInput');   const errorTip = document.getElementById('errorTip');   const loginForm = document.getElementById('loginForm');   // 生成随机字符库:数字+大小写字母   function getRandomChar(){     const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';     return chars[Math.floor(Math.random() * chars.length)];   }   // 生成随机颜色   function getRandomColor(){     const r = Math.floor(Math.random()*150);     const g = Math.floor(Math.random()*150);     const b = Math.floor(Math.random()*150);     return `rgb(${r},${g},${b})`;   }   // 绘制验证码   function drawCode(){     // 清空画布     ctx.clearRect(0,0,canvas.width,canvas.height);     // 背景色     ctx.fillStyle = '#f6f6f6';     ctx.fillRect(0,0,canvas.width,canvas.height);     validCode = '';     // 生成4位验证码     for(let i=0;i<4;i++){       const char = getRandomChar();       validCode += char;       ctx.font = `bold ${24 + Math.random()*6}px Arial`;       ctx.fillStyle = getRandomColor();       // 文字旋转       ctx.save();       const rotate = (Math.random()-0.5)*0.3;       ctx.translate(25+i*25,28);       ctx.rotate(rotate);       ctx.fillText(char,0,0);       ctx.restore();     }     // 画干扰线     for(let i=0;i<4;i++){       ctx.strokeStyle = getRandomColor();       ctx.beginPath();       ctx.moveTo(Math.random()*canvas.width, Math.random()*canvas.height);       ctx.lineTo(Math.random()*canvas.width, Math.random()*canvas.height);       ctx.stroke();     }     // 画干扰点     for(let i=0;i<40;i++){       ctx.fillStyle = getRandomColor();       ctx.beginPath();       ctx.arc(Math.random()*canvas.width,Math.random()*canvas.height,1,0,2*Math.PI);       ctx.fill();     }   }   // 页面初始化绘制验证码   drawCode();   // 点击画布刷新验证码   canvas.addEventListener('click',()=>{     errorTip.innerText = '';     drawCode();   })   // 表单提交拦截   loginForm.addEventListener('submit',function(e){     e.preventDefault(); // 阻止表单原生提交     const userInput = codeInput.value.trim().toUpperCase();     const rightCode = validCode.toUpperCase();     // 判断验证码是否一致     if(!userInput){       errorTip.innerText = '请输入验证码';       return;     }     if(userInput !== rightCode){       errorTip.innerText = '验证码错误,请重新输入';       drawCode(); // 验证码错误刷新验证码       codeInput.value = '';       return;     }     // 验证码校验通过,执行业务提交     errorTip.innerText = '';     alert('验证码校验通过,准备提交登录表单');     // 这里写ajax/fetch提交账号密码逻辑     // fetch('/api/login',{method:'POST',body:xxx})   }) </script> </body> </html>

核心逻辑讲解

1. 验证码生成

利用canvas绘图 API,循环取出随机字符,给每个字符设置随机大小、颜色、旋转角度;增加干扰线和噪点,防止简单脚本截图识别。变量validCode保存在 JS 内存中,存储本次正确验证码。点击 canvas 画布触发重新绘制,实现刷新。

2. 表单拦截与校验

给 form 绑定submit事件,调用e.preventDefault()阻止浏览器默认表单跳转。 拿到用户输入的验证码,统一转大写做对比,忽略大小写差异。

  • 为空:提示输入验证码;

  • 不一致:提示错误,刷新验证码,清空输入框;

  • 一致:提示校验通过,执行登录接口请求。

存在的缺陷与生产环境建议

前端验证码缺点

  1. 验证码保存在前端 JS 变量,懂技术的人打开控制台可以直接读取validCode拿到正确验证码,很容易被绕过;

  2. 无法对抗爬虫脚本,脚本可以直接删掉 JS 校验逻辑,直接发送登录接口请求。

线上项目正确做法

  1. 验证码由后端生成:后端生成随机码,生成图片返回前端,同时把验证码存 session/redis;

  2. 用户登录提交时,把用户输入的验证码传给后端,后端做校验

  3. 前端仅做交互提示,不做可信校验;

  4. 可以保留前端简单校验,优化用户体验,但不能替代服务端校验。

扩展优化方向

  1. 增加验证码长度可配置,比如 4 位 / 6 位;

  2. 增加输入框失去焦点实时校验,不用等到点登录才提示;

  3. 增加验证码过期逻辑,一段时间自动刷新;

  4. 换成滑动验证码、点选验证码提升防机器能力。

总结:前端验证码适合做用户体验优化,减少无效请求,安全校验必须交给后端,这是开发登录模块一定要注意的关键点。

联系尚武科技
客户服务
石家庄APP开发
0311-83796180
为您提供售前购买咨询、解决方案推荐等1V1服务!
技术支持及售后
石家庄APP开发公司
0311-66682288
为您提供从产品到服务的全面技术支持 !
客户服务
石家庄小程序开发
石家庄小程序开发公司
加我企业微信
为您提供售前购买咨询、
解决方案推荐等1V1服务!
石家庄网站建设公司
咨询相关问题或预约面谈,可以通过以下方式与我们联系。
石家庄网站制作
在线联系:
石家庄Web开发
石家庄软件开发
石家庄软件开发公司
ADD/地址:
河北·石家庄
新华区西三庄大街86号河北互联网大厦B座二层
Copyright © 2008-2026尚武科技 保留所有权利。 冀ICP备12011207号-2 石家庄网站开发冀公网安备 13010502001294号《互联网平台公约协议》
Copyright © 2026 www.sw-tech.cn, Inc. All rights reserved