滑动验证码的设计与理解

前端开发 作者: 2024-08-23 00:15:01
简介 在介绍之前,首先一个概念明确一个共识:没有攻不破的网站,只有值不值得。 这意思是说,我们可以尽可能的提高自己网站的安全,但并没有绝对的安全,当网站安全级别大于攻击者能得到的回报时,你的网站就是安

简介


js类的设计

 1 /**
 2  * 验证码的父类,所有验证码都要继承这个类
 3  * @param id 验证码的唯一标识
 4  * @param type 验证码的类型
 5  * @param contentDiv 包含着验证码的DIV
 6  * @constructor
 7  */
 8 var Identifying = function (id,type,contentDiv){
 9     this.id = id;
10     this.type = type;
11     this.contentDiv=contentDiv;
12 }
13 
14 15  * 销毁函数
16  17 Identifying.prototype.destroy = (){
18     this.successFunc = null;
19     this.errorFunc = 20     this.clearDom();
21     this.contentDiv = 22 23 
24 25  * 清除节点内容
26  27 Identifying.prototype.clearDom = 28     if(this.contentDiv instanceof jQuery){
29         .contentDiv.empty();
30     }else  HTMLElement){
31         this.contentDiv.innerText = ""32     }
33 34 
35 36  * 回调函数
37  * 验证成功后进行调用
38  * this需要指具体验证类
39  * @param result 对象,有对应验证类的传递的参数,具体要看验证类
40  41 Identifying.prototype.success =  (result) {
42     this.successFunc  Function){
43         .successFunc(result);
44 45 46 
47 48  * 验证失败发生错误调用的函数
49  * @param result
50  51 Identifying.prototype.error = 52     this.errorFunc 53         .errorFunc(result);
54     }else{
55         //统一处理错误
56 57 58 
59 60  * 获取验证码id
61  62 Identifying.prototype.getId =  () {
63     return .id;
64 65 
66 67  * 获取验证码类型
68  * @returns {*}
69  70 Identifying.prototype.getType = 71     .type;
72 73 
74 75  *  显示验证框
76  77 Identifying.prototype.showIdentifying = (callback){
78     this.contentDiv.show(,callback);
79 80 
81 82  * 隐藏验证框
83  84 Identifying.prototype.hiddenIdentifying = 85     this.contentDiv.hide(86 87 
88 89  * 获得验证码显示的dom元素
90  91 Identifying.prototype.getContentDiv = 92     .contentDiv;
93 }
  1   2  * 滑动验证类
  3  * complete传递的参数为identifyingId,identifyingType,moveEnd_X
  4  * @param config 各种配置
  5    6 var ImgIdentifying = (config) {
  7     Identifying.call(  8     this.config = config;
  9     .init();
 10     .showIdentifying();
 11  12 
 13 继承父类
 14 extendClass(Identifying,ImgIdentifying);
 15 
 16  17  18   19 ImgIdentifying.prototype.destroy =  20     Identifying.prototype.destroy.call();
 21  22 
 23 var width = '260' 24 var height = '116' 25 var pl_size = 48 26 var padding_ = 20 27 ImgIdentifying.prototype.init =  28 
 29      30     var el = .getContentDiv();
 31     var w = width;
 32     var h = height;
 33     var PL_Size = pl_size;
 34     var padding = padding_;
 35     var self =  36 
 37     这个要转移到后台
 38      RandomNum(Min,Max) {
 39         var Range = Max - Min;
 40         var Rand = Math.random();
 41 
 42         if (Math.round(Rand * Range) == 0) {
 43             return Min + 1 44         } if (Math.round(Rand * Max) == Max) {
 45             return Max - 1 46         }  {
 47             var num = Min + Math.round(Rand * Range) - 1 48             return num;
 49         }
 50  51 
 52     确定图片
 53     var imgSrc = .config.img;
 54     var X = .config.X;
 55     var Y = .config.Y;
 56     var left_Num = -X + 10 57     var html = '<div style="position:relative;padding:16px 16px 28px;border:1px solid #ddd;background:#f2ece1;border-radius:16px;">' 58     html += '<div style="position:relative;overflow:hidden;width:' + w + 'px;">' 59     html += '<div style="position:relative;width:' + w + 'px;height:' + h + 'px;">' 60     html += '<img id="scream" src="' + imgSrc + '" style="width:' + w + 'px;height:' + h + 'px;">' 61     html += '<canvas id="puzzleBox" width="' + w + '" height="' + h + '" style="position:absolute;left:0;top:0;z-index:222;"></canvas>' 62     html += '</div>' 63     html += '<div class="puzzle-lost-box" style="position:absolute;width:' + w + 'px;height:' + h + 'px;top:0;left:' + left_Num + 'px;z-index:11111;">' 64     html += '<canvas id="puzzleShadow" width="' + w + '" height="' + h + '" style="position:absolute;left:0;top:0;z-index:222;"></canvas>' 65     html += '<canvas id="puzzleLost" width="' + w + '" height="' + h + '" style="position:absolute;left:0;top:0;z-index:333;"></canvas>' 66     html += '</div>' 67     html += '<p class="ver-tips"></p>' 68     html += '</div>' 69     html += '<div class="re-btn"><a></a></div>' 70     html += '</div>' 71     html += '<br>' 72     html += '<div style="position:relative;width:' + w + 'px;margin:auto;">' 73     html += '<div style="border:1px solid #c3c3c3;border-radius:24px;background:#ece4dd;box-shadow:0 1px 1px rgba(12,10,0.2) inset;">';inset 为内阴影
 74     html += '<p style="font-size:12px;color: #486c80;line-height:28px;margin:0;text-align:right;padding-right:22px;">按住左边滑块,拖动完成上方拼图</p>' 75     html += '</div>' 76     html += '<div class="slider-btn"></div>' 77     html += '</div>' 78 
 79     el.html(html);
 80 
 81     var d = PL_Size / 3 82     var c = document.getElementById("puzzleBox" 83     getContext获取该dom节点的canvas画布元素
 84     ---------------------------------这一块是图片中央缺失的那一块--------------------------------------
 85     var ctx = c.getContext("2d" 86 
 87     ctx.globalCompositeOperation = "xor" 88     设置阴影模糊级别
 89     ctx.shadowBlur = 10 90     设置阴影的颜色
 91     ctx.shadowColor = "#fff" 92     设置阴影距离的水平距离
 93     ctx.shadowOffsetX = 3 94     设置阴影距离的垂直距离
 95     ctx.shadowOffsetY = 3 96     rgba第四个参数是透明度,前三个是三原色,跟rgb比就是多了第四个参数
 97     ctx.fillStyle = "rgba(0,0.8)" 98     beginPath() 方法开始一条路径,或重置当前的路径。
 99     提示:请使用这些方法来创建路径:moveTo()、lineTo()、quadricCurveTo()、bezierCurveTo()、arcTo() 以及 arc()。
100     ctx.beginPath();
101     指线条的宽度
102     ctx.lineWidth = "1"103     strokeStyle 属性设置或返回用于笔触的颜色、渐变或模式
104     ctx.strokeStyle = "rgba(0,0)"105     表示画笔移到(X,Y)位置,没画东西
106     ctx.moveTo(X,Y);
107     画笔才开始移动到指定坐标,之间画一条直线
108     ctx.lineTo(X + d,1)">109     绘制一条贝塞尔曲线,一共四个点确定,开始点(没在参数里),和两个控制点(1和2参数结合,3和4参数结合),结束点(5和6参数结合)
110     ctx.bezierCurveTo(X + d,Y - d,X + 2 * d,X + 2 *111     ctx.lineTo(X + 3 *112     ctx.lineTo(X + 3 * d,Y + d);
113     ctx.bezierCurveTo(X + 2 * d,Y + d,Y + 2 * d,X + 3 * d,Y + 2 *114     ctx.lineTo(X + 3 * d,Y + 3 *115     ctx.lineTo(X,1)">116     必须和beginPath()成对出现
117     ctx.closePath();
118     进行绘制
119     ctx.stroke();
120     根据fillStyle进行填充
121     ctx.fill();
122 
123     ---------------------------------这个为要移动的块------------------------------------------------
124     var c_l = document.getElementById("puzzleLost"125     ---------------------------------这个为要移动的块增加阴影------------------------------------------------
126     var c_s = document.getElementById("puzzleShadow"127     var ctx_l = c_l.getContext("2d"128     var ctx_s = c_s.getContext("2d"129     var img = new Image();
130     img.src = imgSrc;
131 
132     img.onload = 133         从原图片,进行设置处理再显示出来(其实就是设置你想显示图片的位置2和3参数,和框w高h)
134         ctx_l.drawImage(img,0135 136     ctx_l.beginPath();
137     ctx_l.strokeStyle = "rgba(0,1)">138     ctx_l.moveTo(X,1)">139     ctx_l.lineTo(X +140     ctx_l.bezierCurveTo(X + d,1)">141     ctx_l.lineTo(X + 3 *142     ctx_l.lineTo(X + 3 * d,1)">143     ctx_l.bezierCurveTo(X + 2 * d,1)">144     ctx_l.lineTo(X + 3 * d,1)">145     ctx_l.lineTo(X,1)">146     ctx_l.closePath();
147     ctx_l.stroke();
148     带阴影,数字越高阴影越严重
149     ctx_l.shadowBlur = 10150     阴影的颜色
151     ctx_l.shadowColor = "black"152 
153      ctx_l.fill(); 其实加这句就能有阴影效果了,不知道为什么加多个图层
154 
155     分割画布的块
156     ctx_l.clip();
157 
158     ctx_s.beginPath();
159     ctx_s.lineWidth = "1"160     ctx_s.strokeStyle = "rgba(0,1)">161     ctx_s.moveTo(X,1)">162     ctx_s.lineTo(X +163     ctx_s.bezierCurveTo(X + d,1)">164     ctx_s.lineTo(X + 3 *165     ctx_s.lineTo(X + 3 * d,1)">166     ctx_s.bezierCurveTo(X + 2 * d,1)">167     ctx_s.lineTo(X + 3 * d,1)">168     ctx_s.lineTo(X,1)">169     ctx_s.closePath();
170     ctx_s.stroke();
171     ctx_s.shadowBlur = 20172     ctx_s.shadowColor = "black"173     ctx_s.fill();
174 
175     开始时间
176     var beginTime;
177     结束时间
178      endTime;
179     var moveStart = ''180     $(".slider-btn").mousedown( (e) {
181         $(this).css({"background-position": "0 -216px"});
182         moveStart = e.pageX;
183         beginTime =  Date().valueOf();
184     });
185 
186     onmousemove = 187         var e = e || window.event;
188         var moveX =189         var d = moveX - moveStart;
190         if (moveStart == ''191 
192         } 193             if (d < 0 || d > (w - padding - PL_Size)) {
194 
195             } 196                 $(".slider-btn").css({"left": d + 'px',"transition": "inherit"197                 $("#puzzleLost").css({"left": d + 'px',1)">198                 $("#puzzleShadow").css({"left": d + 'px',1)">199             }
200 201     };
202 
203     onmouseup = 204         205         var moveEnd_X = e.pageX -206         var ver_Num = X - 10207         var deviation = self.config.deviation;
208         var Min_left = ver_Num - deviation;
209         var Max_left = ver_Num +210 
211         212 
213         } 214             endTime = 215             if (Max_left > moveEnd_X && moveEnd_X > Min_left) {
216                 $(".ver-tips").html('<i style="background-position:-4px -1207px;"></i><span style="color:#42ca6b;">验证通过</span><span></span>'217                 $(".ver-tips").addClass("slider-tips"218                 $(".puzzle-lost-box").addClass("hidden"219                 $("#puzzleBox").addClass("hidden"220                 setTimeout(221                     $(".ver-tips").removeClass("slider-tips"222                 },2000223                 self.success({
224                     'identifyingId': self.config.identifyingId,'identifyingType': self.config.identifyingType,225                     'moveEnd_X': moveEnd_X
226                 })
227             } 228                 $(".ver-tips").html('<i style="background-position:-4px -1229px;"></i><span style="color:red;">验证失败:</span><span style="margin-left:4px;">拖动滑块将悬浮图像正确拼合</span>'229                 $(".ver-tips").addClass("slider-tips"230                 setTimeout(231                     $(".ver-tips").removeClass("slider-tips"232                 },1)">233                 self.error();
234 235 236         0.5指动画执行到结束一共经历的时间
237         setTimeout(238             $(".slider-btn").css({"left": '0',"transition": "left 0.5s"239             $("#puzzleLost").css({"left": '0',1)">240             $("#puzzleShadow").css({"left": '0',1)">241         },1000242         $(".slider-btn").css({"background-position": "0 -84px"243         moveStart = ''244         $(".re-btn a").on("click",245             Access.getAccess().initIdentifying($('#acessIdentifyingContent'));
246         })
247 248 249 
250 251  * 获取该类型验证码的一些参数
252  253 ImgIdentifying.getParamMap = 254 
255     var min_X = padding_ +256     var max_X = width - padding_ - pl_size - pl_size / 6257     var max_Y =258     var min_Y = height - padding_ - pl_size - pl_size / 6259 
260     var paramMap =  Map();
261     paramMap.set("min_X"262     paramMap.set("max_X"263     paramMap.set("min_Y"264     paramMap.set("max_Y"265 
266      paramMap;
267 268 
269 270  * 设置验证成功的回调函数
271  * @param success
272  273 ImgIdentifying.prototype.setSuccess =  (successFunc) {
274     this.successFunc = successFunc;
275 276 
277 278  * 设置验证失败的回调函数
279 280  281 ImgIdentifying.prototype.setError =  (errorFunc) {
282     this.errorFunc = errorFunc;
283 }

后端的设计

/**
 * 验证码类的接口,所有验证码必须继承此接口
 3  public interface I_Identifying<T> 5 
 6     String EXCEPTION_CODE = SystemStaticValue.IDENTIFYING_EXCEPTION_CODE;
 7     String IDENTIFYING = "Identifying" 8     --------------以下为验证码大体错误类型,抛出错误时候用,会传至前端---------------
验证成功
10     String SUCCESS = "Success"验证失败
12     String FAILURE = "Failure"13     验证码过期
14     String OVERDUE = "Overdue"15 
16     -------以下为验证码具体错误类型,存放在checkResult-------------
17     String PARAM_ERROR = "验证码参数错误"18     String OVERDUE_ERROR = "验证码过期"19     String TYPE_ERROR = "验证码业务类型错误"20     String ID_ERROR = "验证码id异常"21     String CHECK_ERROR = "验证码验证异常"22 
24          * 获取生成好的验证码
26      * @param request
27 @return
28      29     public T getInstance(HttpServletRequest request) throws Exception;
30 
31          * 进行验证,没抛异常说明验证无误
34      35     void checkIdentifying(HttpServletRequest request) 36 
37          * 获取验证结果,如果成功则为success,失败则为失败信息
40      41     public String getCheckResult();
42 
43          * 获取验证码的业务类型
46      47      String getIdentifyingType();
48 }
 * @author NiceBin
 * @description: 验证码类,前端需要生成验证码的信息
 * @date 2019/7/12 16:04
 5  class ImgIdentifying implements I_Identifying<ImgIdentifying> 7     此次验证码的id
private String identifyingId;
此次验证码的业务类型
 String identifyingType;
需要使用的图片
12      String imgSrc;
生成块的x坐标
14     private int X;
15     生成块的y坐标
 Y;
17     允许的误差
int deviation = 2验证码生成的时间
 Calendar calendar;
验证码结果,如果有结果说明已经被校验,防止因为网络延时的二次校验
22      String checkResult;
下面是逻辑代码...
25 }
 * @description: 验证码过滤器,帮忙验证有需要验证码的请求,不帮忙生成验证码
 * @date 2019/7/23 15:06
@Component
 7 class IdentifyingInterceptor implements HandlerInterceptor {
    @Override
boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler)  Exception {
10 
11         HttpSession session = request.getSession();
12         I_Identifying identifying= (I_Identifying)session.getAttribute(I_Identifying.IDENTIFYING);
13         if(identifying!=){
            identifying.checkIdentifying(request);
15         }16             应该携带验证码信息的,结果没有携带,那就是个非法请求
17             false18 19         true20 
21 23 void postHandle(HttpServletRequest request,Object handler,ModelAndView modelAndView) 25 
27 
28 void afterCompletion(HttpServletRequest request,Exception ex) 31 32 }
View Code
原创声明
本站部分文章基于互联网的整理,我们会把真正“有用/优质”的文章整理提供给各位开发者。本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
本文链接:http://www.jiecseo.com/news/show_66948.html