Springboot基于前端和后端验证码的实现和校验

2022-08-24 10:28:43 浏览数 (1)

前端

  以前在学Struts2的时候,基于JSP的纯前端用过js生成验证码和校验,但是这种容易被绕过不够安全,这个也就一些方面完善了一些不足。工具类参考于网络,其他的代码也比较简单,希望能起到参考作用!

代码语言:javascript复制
<input type="text" name="verification" id="verification" class="form-control" placeholder="请输入验证码"> 
<img id="imgVerify" src="" onclick="getVerify();" alt="点击更换验证码"/><a href="#" onclick="getVerify();" rel="nofollow">看不清,换一张</a> 
<script type="text/javascript"> 
    $(document.body).ready(function () { 
 //首次获取验证码 
        $("#imgVerify").attr("src","/getVerify?" Math.random()); 
 }); 
 //获取验证码 
 function getVerify() { 
 var src1 = document.getElementById('imgVerify') 
        src1.src = "/getVerify?"   Math.random(); 
 } 
</script> 
<!--<script th:inline="javascript"> 
 /*<![CDATA[*/ 
 //校验验证码 
 function checkCode() { 
 var code=/*[[${session.RANDOMVALIDATECODEKEY}]]*/ ; 
 var checkCode = $("#verification").val().toLowerCase(); 
 if(checkCode==code){ 
            $("#loginform").submit(); 
 }else{ 
            alert("验证码错误"); 
 } 
 } 
 /*]]>*/ 
</script>--> 
//这里采用前端校验,因为页面获取的刷新前的一次Session值,所以这里会出现问题。 
<script type="text/javascript"> 
 function checkCode() { 
 var checkCode = $("#verification").val().toLowerCase(); 
        $.post("/checkCode",{'checkCode': checkCode},function(result){ 
 if(result.success=="true"){ 
                $("#loginform").submit(); 
 }else{ 
                alert(result.errorInfo); 
 } 
 },"json"); 
 } 
</script> 

Controller

代码语言:javascript复制
@ResponseBody 
@RequestMapping(value = "/getVerify") 
public void getVerify(HttpServletRequest request, HttpServletResponse response) { 
    response.setContentType("image/jpeg");//设置相应类型,告诉浏览器输出的内容为图片 
    response.setHeader("Pragma", "No-cache");//设置响应头信息,告诉浏览器不要缓存此内容 
    response.setHeader("Cache-Control", "no-cache"); 
    response.setDateHeader("Expire", 0); 
 RandomValidateCode randomValidateCode = new RandomValidateCode(); 
 try { 
        randomValidateCode.getRandcode(request, response);//输出验证码图片方法 
 } catch (Exception e) { 
        e.printStackTrace(); 
 } 
} 
@ResponseBody 
@RequestMapping("/checkCode") 
public Map<String,String> save(@RequestParam("checkCode") String imageCode, HttpServletRequest request, 
 HttpServletResponse response, HttpSession session) throws Exception { 
 String verifyCode = (String) session.getAttribute("RANDOMVALIDATECODEKEY"); 
 HashMap<String, String> result = new HashMap<>(); 
 if(!imageCode.equals(verifyCode)){ 
        result.put("errorInfo", "验证码填写错误!"); 
 }else{ 
        result.put("success", "true"); 
 } 
 return  result; 
} 

工具类

代码语言:javascript复制
public class RandomValidateCode { 
 public static final String RANDOMCODEKEY= "RANDOMVALIDATECODEKEY";//放到session中的key 
 //private String randString = "0123456789";//随机产生只有数字的字符串 private String 
 //private String randString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";//随机产生只有字母的字符串 
 private String randString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//随机产生数字与字母组合的字符串 
 private int width = 95;// 图片宽 
 private int height = 25;// 图片高 
 private int lineSize = 40;// 干扰线数量 
 private int stringNum = 4;// 随机产生字符数量 
 private Random random = new Random(); 
 /* 
     * 获得字体 
     */ 
 private Font getFont() { 
 return new Font("Fixedsys", Font.CENTER_BASELINE, 18); 
 } 
 /* 
     * 获得颜色 
     */ 
 private Color getRandColor(int fc, int bc) { 
 if (fc > 255) 
            fc = 255; 
 if (bc > 255) 
            bc = 255; 
 int r = fc   random.nextInt(bc - fc - 16); 
 int g = fc   random.nextInt(bc - fc - 14); 
 int b = fc   random.nextInt(bc - fc - 18); 
 return new Color(r, g, b); 
 } 
 /** 
     * 生成随机图片 
     */ 
 public void getRandcode(HttpServletRequest request, HttpServletResponse response) { 
 HttpSession session = request.getSession(); 
 // BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类 
 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR); 
 Graphics g = image.getGraphics();// 产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作 
        g.fillRect(0, 0, width, height); 
        g.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE, 18)); 
        g.setColor(getRandColor(110, 133)); 
 // 绘制干扰线 
 for (int i = 0; i <= lineSize; i  ) { 
            drowLine(g); 
 } 
 // 绘制随机字符 
 String randomString = ""; 
 for (int i = 1; i <= stringNum; i  ) { 
            randomString = drowString(g, randomString, i); 
 } 
        session.removeAttribute(RANDOMCODEKEY); 
 //验证码转换为小写,实现前端校验不区分大小写 
        session.setAttribute(RANDOMCODEKEY, randomString.toLowerCase()); 
        g.dispose(); 
 try { 
 // 将内存中的图片通过流动形式输出到客户端 
 ImageIO.write(image, "JPEG", response.getOutputStream()); 
 } catch (Exception e) { 
            e.printStackTrace(); 
 } 
 } 
 /* 
     * 绘制字符串 
     */ 
 private String drowString(Graphics g, String randomString, int i) { 
        g.setFont(getFont()); 
        g.setColor(new Color(random.nextInt(101), random.nextInt(111), random 
 .nextInt(121))); 
 String rand = String.valueOf(getRandomString(random.nextInt(randString 
 .length()))); 
        randomString  = rand; 
        g.translate(random.nextInt(3), random.nextInt(3)); 
        g.drawString(rand, 13 * i, 16); 
 return randomString; 
 } 
 /* 
     * 绘制干扰线 
     */ 
 private void drowLine(Graphics g) { 
 int x = random.nextInt(width); 
 int y = random.nextInt(height); 
 int xl = random.nextInt(13); 
 int yl = random.nextInt(15); 
        g.drawLine(x, y, x   xl, y   yl); 
 } 
 /* 
     * 获取随机的字符 
     */ 
 public String getRandomString(int num) { 
 return String.valueOf(randString.charAt(num)); 
 } 
} 

简单的验证码功能大功告成!

 那么有时候我们不想在后端校验,只想在前端做一个处理怎么办,那就用js绘制验证码,下面一个js一个 css拷贝下来,后面需要引入:

js

代码语言:javascript复制
/*! Verify-v0.1.0 MIT License by younggc@foxmail.com*/ 
;(function($, window, document,undefined) { 
 //定义Code的构造函数 
 var Code = function(ele, opt) { 
 this.$element = ele, 
 this.defaults = { 
                type : 1, 
                figure : 100, //位数,仅在type=2时生效 
                arith : 0, //算法,支持加减乘,0为随机,仅在type=2时生效 
                width : '200px', 
                height : '60px', 
                fontSize : '30px', 
                codeLength : 6, 
                btnId : 'check-btn', 
                ready : function(){}, 
                success : function(){}, 
                error : function(){} 
 }, 
 this.options = $.extend({}, this.defaults, opt) 
 }; 
 var _code_chars = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; 
 var _code_color1 = ['#fffff0', '#f0ffff', '#f0fff0', '#fff0f0']; 
 var _code_color2 = ['#FF0033', '#006699', '#993366', '#FF9900', '#66CC66', '#FF33CC']; 
 //定义Code的方法 
 Code.prototype = { 
        init : function() { 
 var _this = this; 
 this.loadDom(); 
 this.setCode(); 
 this.options.ready(); 
 this.$element[0].onselectstart = document.body.ondrag = function(){ 
 return false; 
 }; 
 //点击验证码 
 this.$element.find('.verify-code, .verify-change-code').on('click', function() { 
                _this.setCode(); 
 }); 
 //确定的点击事件 
 this.htmlDoms.code_btn.on('click', function() { 
                _this.checkCode(); 
 }) 
 }, 
 //加载页面 
        loadDom : function() { 
 var panelHtml = '<div class="cerify-code-panel"><div class="verify-code"></div><div class="verify-code-area"><div class="verify-input-area"><input type="text" class="varify-input-code" /></div><div class="verify-change-area"><a class="verify-change-code">换一张</a></div></div></div>'; 
 this.$element.append(panelHtml); 
 this.isEnd = false; 
 this.htmlDoms = { 
                code_btn : $('#' this.options.btnId), 
                code : this.$element.find('.verify-code'), 
                code_area : this.$element.find('.verify-code-area'), 
                code_input : this.$element.find('.varify-input-code'), 
 }; 
 this.htmlDoms.code.css({'width':this.options.width, 'height':this.options.height,'line-height':this.options.height, 'font-size':this.options.fontSize}); 
 this.htmlDoms.code_area.css({'width':this.options.width}); 
 }, 
 //设置验证码 
        setCode : function() { 
 if(this.isEnd == false) { 
 var color1Num = Math.floor(Math.random() * 3); 
 var color2Num = Math.floor(Math.random() * 5); 
 this.htmlDoms.code.css({'background-color': _code_color1[color1Num], 'color': _code_color2[color2Num]}); 
 this.htmlDoms.code_input.val(''); 
 var code = ''; 
 this.code_chose = ''; 
 if(this.options.type == 1) { //普通验证码 
 for(var i = 0; i < this.options.codeLength; i  ) { 
 var charNum = Math.floor(Math.random() * 52); 
 var tmpStyle = (charNum%2 ==0)? "font-style:italic;margin-right: 10px;":"font-weight:bolder;"; 
                        tmpStyle  = (Math.floor(Math.random() * 2) == 1)? "font-weight:bolder;":""; 
 this.code_chose  = _code_chars[charNum]; 
                        code  = '<font style="' tmpStyle '">' _code_chars[charNum] '</font>'; 
 } 
 }else { //算法验证码 
 var num1 = Math.floor(Math.random() * this.options.figure); 
 var num2 = Math.floor(Math.random() * this.options.figure); 
 if(this.options.arith == 0) { 
 var tmparith = Math.floor(Math.random() * 3); 
 } 
 switch(tmparith) { 
 case 1 : 
 this.code_chose = parseInt(num1)   parseInt(num2); 
                            code = num1   '   '   num2   ' = ?'; 
 break; 
 case 2 : 
 if(parseInt(num1) < parseInt(num2)) { 
 var tmpnum = num1; 
                                num1 = num2; 
                                num2 = tmpnum; 
 } 
 this.code_chose = parseInt(num1) - parseInt(num2); 
                            code = num1   ' - '   num2   ' = ?'; 
 break; 
 default : 
 this.code_chose = parseInt(num1) * parseInt(num2); 
                            code = num1   ' × '   num2   ' = ?'; 
 break; 
 } 
 } 
 this.htmlDoms.code.html(code); 
 } 
 }, 
 //比对验证码 
        checkCode : function() { 
 if(this.options.type == 1) { //普通验证码 
 var own_input = this.htmlDoms.code_input.val().toUpperCase(); 
 this.code_chose = this.code_chose.toUpperCase(); 
 }else { 
 var own_input = this.htmlDoms.code_input.val(); 
 } 
 if(own_input == this.code_chose) { 
 this.isEnd = true; 
 this.options.success(this); 
 }else { 
 this.options.error(this); 
 this.setCode(); 
 } 
 }, 
 //刷新 
        refresh : function() { 
 this.isEnd = false; 
 this.$element.find('.verify-code').click(); 
 } 
 }; 
 //在插件中使用codeVerify对象 
    $.fn.codeVerify = function(options, callbacks) { 
 var code = new Code(this, options); 
        code.init(); 
 }; 
}) 
(jQuery, window, document); 

css

代码语言:javascript复制
/*常规验证码*/ 
.verify-code { 
    font-size: 20px; 
    text-align: center; 
    cursor: pointer; 
    margin-bottom: 5px; 
    border: 1px solid #ddd; 
} 
.cerify-code-panel { 
    height:100%; 
    overflow:hidden; 
} 
.verify-code-area { 
 float:left; 
} 
.verify-input-area { 
 float: left; 
    width: 60%; 
    padding-right: 10px; 
} 
.verify-change-area { 
    line-height: 30px; 
 float: left; 
} 
.varify-input-code { 
    display:inline-block; 
    width: 100%; 
    height: 25px; 
} 
.verify-change-code { 
    color: #337AB7; 
    cursor: pointer; 
} 
.verify-btn { 
    width: 200px; 
    height: 30px; 
    background-color: #337AB7; 
    color:#FFFFFF; 
    border:none; 
    margin-top: 10px; 
} 
/*滑动验证码*/ 
.verify-bar-area { 
    position: relative; 
    background: #FFFFFF; 
    text-align: center; 
 -webkit-box-sizing: content-box; 
 -moz-box-sizing: content-box; 
    box-sizing: content-box; 
    border: 1px solid #ddd; 
 -webkit-border-radius: 4px; 
} 
.verify-bar-area .verify-move-block { 
    position: absolute; 
    top: 0px; 
    left: 0; 
    background: #fff; 
    cursor: pointer; 
 -webkit-box-sizing: content-box; 
 -moz-box-sizing: content-box; 
    box-sizing: content-box; 
    box-shadow: 0 0 2px #888888; 
 -webkit-border-radius: 1px; 
} 
.verify-bar-area .verify-move-block:hover { 
    background-color: #337ab7; 
    color: #FFFFFF; 
} 
.verify-bar-area .verify-left-bar { 
    position: absolute; 
    top: -1px; 
    left: -1px; 
    background: #f0fff0; 
    cursor: pointer; 
 -webkit-box-sizing: content-box; 
 -moz-box-sizing: content-box; 
    box-sizing: content-box; 
    border: 1px solid #ddd; 
} 
.verify-img-panel { 
    margin:0; 
 -webkit-box-sizing: content-box; 
 -moz-box-sizing: content-box; 
    box-sizing: content-box; 
    border: 1px solid #ddd; 
    border-radius: 3px; 
    position: relative; 
} 
.verify-img-panel .verify-refresh { 
    width: 25px; 
    height: 25px; 
    text-align:center; 
    padding: 5px; 
    cursor: pointer; 
    position: absolute; 
    top: 0; 
    right: 0; 
    z-index: 2; 
} 
.verify-img-panel .icon-refresh { 
    font-size: 20px; 
    color: #fff; 
} 
.verify-img-panel .verify-gap { 
    background-color: #fff; 
    position: relative; 
    z-index: 2; 
    border:1px solid #fff; 
} 
.verify-bar-area .verify-move-block .verify-sub-block { 
    position: absolute; 
    text-align: center; 
    z-index: 3; 
    border: 1px solid #fff; 
} 
.verify-bar-area .verify-move-block .verify-icon { 
    font-size: 18px; 
} 
.verify-bar-area .verify-msg { 
    z-index : 3; 
} 
/*字体图标的css*/ 
@font-face {font-family: "iconfont"; 
    src: url('../fonts/iconfont.eot?t=1508229193188'); /* IE9*/ 
    src: url('../fonts/iconfont.eot?t=1508229193188#iefix') format('embedded-opentype'), /* IE6-IE8 */ 
    url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAaAAAsAAAAACUwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZW7kiSY21hcAAAAYAAAAB3AAABuM qBlRnbHlmAAAB AAAAnQAAALYnrUwT2hlYWQAAARsAAAALwAAADYPNwajaGhlYQAABJwAAAAcAAAAJAfeA4dobXR4AAAEuAAAABMAAAAYF kAAGxvY2EAAATMAAAADgAAAA4CvAGsbWF4cAAABNwAAAAfAAAAIAEVAF1uYW1lAAAE/AAAAUUAAAJtPlT fXBvc3QAAAZEAAAAPAAAAE3oPPXPeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2Bk/sM4gYGVgYOpk kMAwNDP4RmfM1gxMjBwMDEwMrMgBUEpLmmMDgwVDxbwtzwv4EhhrmBoQEozAiSAwAw1A0UeJzFkcENgCAMRX8RjCGO4gTe9eQcnhzAfXC2rqG/hYsT8MmD9gdS0gJIAAaykAjIBYHppCvuD8juR6zMJ67A89Zdn/f1aNPikUn8RvYo8G20CjKim6Rf6b9m34 WWd/vBr oW8V6q3vF5qKlYrPRp4L0Ad5nGL8AeJxFUc9rE0EYnTezu8lMsrvtbrqb3TRt0rS7bdOmdI0JbWmCtiItIv5oi14qevCk9SQVLFiQgqAF8Q9QLKIHLx48FkHo3ZNnFUXwD5C2B6dO6sFhmI83w7z3fe8RnZCjb2yX5YlLhskkmScXCIFRxYBFiyjH9Rqtoqes9/g5i8WVuJyqDNTYLPwBI cljXrkGynDhoU nCgnjbhGY5yst gMEq8IBIXwsjPU67CnEPm4b0su0h309Fd67da4XBhr55KSm17POk7gOE/Shq6nKdVsC7d9j tcGPKVboc9u/0jtB/ZIA7PXTVLBef6o/paccjnwOYm3ELJetPuDrvV3gg91wlSXWY6H5qVwRzWf2TybrYYfSdqoXOwh/Qa8RWIjBTiSI3h614/vKSNRhONOrsnQi6Xf4nQFQDTmJE1NKbhI6crHEJO/ S5QPxhYJRRyvBFBP 5T9EPpEAIVzzRQIrjmJ6jY1WTo NXTMchuBsKuS8PRZATSMl9oTA4uNLkeIA0V1UeqOoGQh7IAxGo 7T83fn3T voqCNPPAUazUYUI7LgKSV1Jk2oUeghYGhZ cKOe2FjVu5ZKEY2VkE13AK1 jI4r1KLbPlZfrKiPhOXKPRj7q9sj9XJ7LFHNmrKJS3VCdhXGSdKrtmoQaWeMjQVt0KD6sGPOx0oH2fgtzoNROxtNq8F3tzYM/n TjKSX5qf2jx941276TIr9FjXxKr8eX/6bK4yuopwo9py1sw8F9kdw4AmurRpLUM3tYx5ZnKpfHPi8dzz19vJ6MjyxYUrpqeb1uLs3eGV6vr21pSqpeWkqonAN9oUyIiXpv8XvlN5e3icY2BkYGAA4n0vN4fG89t8ZeBmYQCBa9wPPRH0/wcsDMwmQC4HAxNIFABAfAqaAHicY2BkYGBu N/AEMPCAAJAkpEBFbABAEcMAm94nGNhYGBgfsnAwMKAigESnwEBAAAAAAAAdgCkANoBCAFsAAB4nGNgZGBgYGMIZGBlAAEmIOYCQgaG/2A AwARSAFzAHicZY9NTsMwEIVf gekEqqoYIfkBWIBKP0Rq25YVGr3XXTfpk6bKokjx63UA3AejsAJOALcgDvwSCebNpbH37x5Y08A3OAHHo7fLfeRPVwyO3INF7gXrlN/EG6QX4SbaONVuEX9TdjHM6bCbXRheYPXuGL2hHdhDx18CNdwjU/hOvUv4Qb5W7iJO/wKt9Dx6sI 5l5XuI1HL/bHVi cXqnlQcWhySKTOb CmV7vkoWt0uqca1vEJlODoF9JU51pW91T7NdD5yIVWZOqCas6SYzKrdnq0AUb5/JRrxeJHoQm5Vhj/rbGAo5xBYUlDowxQhhkiMro6DtVZvSvsUPCXntWPc3ndFsU1P9zhQEC9M9cU7qy0nk6T4E9XxtSdXQrbsuelDSRXs1JErJCXta2VELqATZlV44RelzRiT8oZ0j/AAlabsgAAAB4nGNgYoAALgbsgI2RiZGZkYWRlZGNkZ2BsYI1OSM1OZs1OSe/OJW1KDM9o4S9KDWtKLU4g4EBAJ79CeQ=') format('woff'), 
    url('../fonts/iconfont.ttf?t=1508229193188') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2 */ 
    url('../fonts/iconfont.svg?t=1508229193188#iconfont') format('svg'); /* iOS 4.1- */ 
} 
.iconfont { 
    font-family:"iconfont" !important; 
    font-size:16px; 
    font-style:normal; 
 -webkit-font-smoothing: antialiased; 
 -moz-osx-font-smoothing: grayscale; 
} 
.icon-check:before { content: "e645"; } 
.icon-close:before { content: "e646"; } 
.icon-right:before { content: "e6a3"; } 
.icon-refresh:before { content: "e6a4"; } 

后面呢就是前端展示了:

代码语言:javascript复制
<div id="m" > 
</div> 
<button type="button" id="check-btn" class="verify-btn">登录</button>--> 

现在在html的head中引入css,引入的上面的css保存的文件:

代码语言:javascript复制
 <link rel="stylesheet" type="text/css" th:href="@{/css/xx.css}"> 

再引入保存的js文件,这里一定要记得的是,juquery的js要放在我们自己的js上面哦!

代码语言:javascript复制
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script> 
<script type="text/javascript" th:src="@{/js/vercode.js}" ></script> 

对应上面div部门的函数:

代码语言:javascript复制
$('#m').codeVerify({ 
            type : 1, 
            width : '120px', 
            height : '40px', 
            fontSize : '30px', 
            codeLength : 4, 
            btnId : 'check-btn', 
            ready : function() { 
 }, 
            success : function() { 
                alert('验证成功!'); 
 }, 
            error : function() { 
                alert('验证码错误!'); 
 } 
 }); 

这样简单的前端验证码就搭建好了。

0 人点赞