在JSP中动态生成随机验证码,登录时后台校验验证码,以及如何避免同一个验证码被重复提交爆破密码

简介:

只需几步就可以生成动态随机的验证码,最终效果如下图:

wKioL1YscJvQIeqDAAF7uMWlQBA873.jpg


一 前台显示页面login.jsp

  其中验证码显示的是一张图片,链接指向的是生成验证码的servlet,同时点击图片后触发changeImg()这个js函数,使其动态生成一个新的验证码,这个函数中的参数t=Math.random()并不会参与验证码的生成,它的作用仅仅只是表示每次提交的并不是同一个请求,需要单独处理,完整的login.jsp代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<%@ page language="java" contentType="text/html; charset=UTF-8"
     pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
< html >
< head >
< meta  http-equiv = "Content-Type"  content = "text/html; charset=UTF-8" >
< base  href="<%=basePath%>">
<%@ include file="head.txt"%>
< script  type = "text/javascript" >
     //刷新验证码
     function changeImg(){
         document.getElementById("validateCodeImg").src="helpDrawValidateCode?t=" + Math.random();
     }
</ script >
</ head >
< body >
     < font  size = 3 >
         < center >
             < form  action = "helpLogin"  name = ""  method = "post" >
                 < table >
                 < tr >< th >请您登陆:</ th ></ tr >
                     < tr >< td  align = left >会员名称:</ td >< td >< input  type = text  name = "id" ></ td ></ tr >
                     < tr >< td  align = left >输入密码:</ td >< td >< input  type = password  name = "password" ></ td ></ tr >              
                 </ table
                 < br >验证码:< input  type = "text"  name = "validateCode"  style = "width:50px" >
                 < img  alt = "看不清?换一张"  src = "helpDrawValidateCode"  id = "validateCodeImg"  onclick = "changeImg()" >                        
                 < br >< input  type = "submit"  value = "提交" >
             </ form >
         </ center >
     </ font >
</ body >
</ html >


二 修改web.xml

  新增一个节点,代码如下:

1
2
3
4
5
6
7
8
9
<!-- 验证码绘制 -->
   < servlet >
     < servlet-name >drawValidateCode</ servlet-name >
     < servlet-class >com.zifangsky.OnlineFriend.servlet.member.HandleDrawValidateCode</ servlet-class >
   </ servlet >
   < servlet-mapping >
     < servlet-name >drawValidateCode</ servlet-name >
     < url-pattern >/helpDrawValidateCode</ url-pattern >
   </ servlet-mapping >


三 后台的servlet文件HandleDrawValidateCode.java

  这个文件主要负责处理前台请求,并返回生成的验证码图片,同时将图片上的随机字符存入session中,以供登录时进行验证,HandleDrawValidateCode.java完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package  com.zifangsky.OnlineFriend.servlet.member;
 
import  java.awt.Color;
import  java.awt.Font;
import  java.awt.Graphics;
import  java.awt.Graphics2D;
import  java.awt.image.BufferedImage;
import  java.io.IOException;
import  java.util.Random;
 
import  javax.imageio.ImageIO;
import  javax.servlet.ServletConfig;
import  javax.servlet.ServletException;
import  javax.servlet.http.HttpServlet;
import  javax.servlet.http.HttpServletRequest;
import  javax.servlet.http.HttpServletResponse;
import  javax.servlet.http.HttpSession;
 
/**
  * 生成随机图片用做验证码
  * */
public  class  HandleDrawValidateCode  extends  HttpServlet{
     private  static  final  long  serialVersionUID = 1L;
     private  static  final  int  WIDTH =  120 ;   //图片宽度
     private  static  final  int  HEIGHT =  30 ;   //图片高度
         
     public  void  init(ServletConfig config)  throws  ServletException{
         super .init(config);
     }
     
     public  void  doPost(HttpServletRequest request,HttpServletResponse response)  throws  ServletException,IOException{
         request.setCharacterEncoding( "utf-8" );
         response.setCharacterEncoding( "utf-8" );
 
         HttpSession session = request.getSession( true );
         
         //创建一张图片
         BufferedImage bufferedImage =  new  BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
         //得到图片
         Graphics graphics = bufferedImage.getGraphics();
         //设置图片背景色
         setBackGround(graphics);
         //设置图片边框
         setBordor(graphics);
         //在图片上画干扰线,用了4种颜色,共20条线条
         drawRandomLine(graphics,Color.GREEN);
         drawRandomLine(graphics, new  Color( 246 , 255 , 145 ));
         drawRandomLine(graphics, new  Color( 225 , 174 , 252 ));
         drawRandomLine(graphics, new  Color( 120 , 202 , 254 ));
         //在图片上写随机字符,并记录生成的序列
         String randomText = drawRandomText((Graphics2D) graphics);
         //将生成的字符存入session中
         session.setAttribute( "checkcode" , randomText);
         //设置响应头通知浏览器以图片的形式打开
         response.setContentType( "image/jpeg" );
         //设置响应头控制浏览器不要缓存
         response.setDateHeader( "expries" , - 1 );
         response.setHeader( "Cache-Control" "no-cache" );
         response.setHeader( "Pragma" "no-cache" );
         //将图片写给浏览器
         ImageIO.write(bufferedImage,  "jpg" , response.getOutputStream());
 
     }
     
     /**
      * 设置图片背景色
      * */
     private  void  setBackGround(Graphics graphics) {
         graphics.setColor(Color.WHITE);
         graphics.fillRect( 0 0 , WIDTH, HEIGHT);
     }
 
     /**
      * 设置图片边框
      * */
     private  void  setBordor(Graphics graphics) {
         graphics.setColor(Color.BLUE);
         graphics.drawRect( 1 1 , WIDTH -  2 , HEIGHT -  2 );
     }
     
     /**
      * 在图片上画干扰线
      * */
     private  void  drawRandomLine(Graphics graphics,Color color) {
         graphics.setColor(color);
         //设置线条个数并画线
         for ( int  i =  0 ;i <  5 ;i++){
             int  x1 =  new  Random().nextInt(WIDTH);
             int  x2 =  new  Random().nextInt(WIDTH);
             int  y1 =  new  Random().nextInt(HEIGHT);
             int  y2 =  new  Random().nextInt(HEIGHT);
             graphics.drawLine(x1, y1, x2, y2);
         }
     }
     
     /**
      * 在图片上写随机字符,数字和字母的组合
      * @param length 字符串的长度
     
      * @return 返回生成的字符串序列
      * */
     private  String drawRandomText(Graphics2D graphics) {
         graphics.setColor(Color.RED);
         graphics.setFont( new  Font( "宋体" , Font.BOLD,  20 ));
         
         //数字和字母的组合
         String baseNumLetter =  "123456789ABCDEFGHJKLMNPQRSTUVWXYZ" ;
         StringBuffer sBuffer =  new  StringBuffer();
         
         int  x =  5 ;   //旋转原点的 x 坐标
         String ch =  "" ;
         Random random =  new  Random();
         for ( int  i =  0 ;i <  4 ;i++){
             //设置字体旋转角度
             int  degree = random.nextInt() %  30 ;   //角度小于30度
             int  dot = random.nextInt(baseNumLetter.length());
             ch = baseNumLetter.charAt(dot) +  ""
             sBuffer.append(ch);
             
             //正向旋转
             graphics.rotate(degree * Math.PI /  180 , x,  20 );
             graphics.drawString(ch, x,  20 );
             
             //反向旋转
             graphics.rotate(-degree * Math.PI /  180 , x,  20 );
             x +=  30 ;
         }
         
         return  sBuffer.toString();
     }
 
     
     public  void  doGet(HttpServletRequest request,HttpServletResponse response)  throws  ServletException,IOException{
         doPost(request, response);
     }
}


注:

  在这里,干扰线的颜色和数目都可以自己设定,颜色可以使用随机色,同时显示的每个文字也可以使用随机的颜色,可以增加验证码识别难度,当然在这里我把比较容易混淆的0和O以及I都给去掉了。如果选用经过编码的中文字符的话,也是可以生成中文验证码的


四 login.jsp页面提交表单后,后台的servlet文件HandleLogin.java校验验证码,并进行登录验证

   经过上面的三个步骤后,login.jsp应该是可以正确显示验证码了,同时点击验证码图片后会生成一个新的验证码,login.jsp提交表单后HandleLogin.java文件验证验证码时主要是:将输入的验证码中的小写字母转换成大写,再和验证码生成时保存在session中的字符串比较,如果相同,则表示输入正确,同时移除session中设置的值,防止重复提交,爆破密码,HandleLogin.java相关代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package  com.zifangsky.OnlineFriend.servlet.member;
 
import  java.io.IOException;
import  java.sql.Connection;
import  java.sql.PreparedStatement;
import  java.sql.ResultSet;
import  java.sql.SQLException;
import  javax.servlet.RequestDispatcher;
import  javax.servlet.ServletConfig;
import  javax.servlet.ServletException;
import  javax.servlet.http.HttpServlet;
import  javax.servlet.http.HttpServletRequest;
import  javax.servlet.http.HttpServletResponse;
import  javax.servlet.http.HttpSession;
 
import  com.zifangsky.OnlineFriend.model.member.Login;
import  com.zifangsky.OnlineFriend.util.DbConn;
import  com.zifangsky.OnlineFriend.util.StringUtil;
 
public  class  HandleLogin  extends  HttpServlet{
     private  String backNews =  "" ;   //登录状态返回信息
     
     public  void  init(ServletConfig config)  throws  ServletException{
         super .init(config);
     }
     
     public  void  doPost(HttpServletRequest request,HttpServletResponse response)  throws  ServletException,IOException{
         request.setCharacterEncoding( "utf-8" );
         response.setCharacterEncoding( "utf-8" );
         HttpSession session = request.getSession( true );
         
         //获取验证码
         String validateCode = StringUtil.xssEncode(request.getParameter( "validateCode" ).trim());
         Object checkcode = session.getAttribute( "checkcode" );
         //将输入的验证码中的小写字母转换成大写,再和验证码生成时保存在session中的字符串比较     
         if (checkcode !=  null  && checkcode.equals(StringUtil.convertToCapitalString(validateCode))){
             session.removeAttribute( "checkcode" );
             continueDoPost(request,response);
         }
         else {          
             response.sendRedirect( "login.jsp" );
             return ;
         }
             
     }
     
     private  void  continueDoPost(HttpServletRequest request, HttpServletResponse response)  throws  ServletException,IOException{
         //这里的登录后续操作省略
     }
 
     public  void  doGet(HttpServletRequest request,HttpServletResponse response)  throws  ServletException,IOException{
         doPost(request, response);
     }
}


注1:上面用到的StringUtil是我写的一个字符串相关的方法类,其中用到的方法convertToCapitalString()目的是将一个字符串中的小写字母转换为大写字母,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
      * 将一个字符串中的小写字母转换为大写字母
     
      * */
     public  static  String convertToCapitalString(String src)
     {
          char [] array = src.toCharArray();
           int  temp =  0 ;
           for  ( int  i =  0 ; i < array.length; i++)
           {
               temp = ( int ) array[i];
               if  (temp <=  122  && temp >=  97 ){  // array[i]为小写字母
                    array[i] = ( char ) (temp -  32 );
               }
            }
            return  String.valueOf(array);
        }


注2:其中方法xssEncode()是将输入的字符串进行编码,以此避免产生XSS跨站脚本攻击,用的是commons-lang-2.4.jar里的StringEscapeUtils,代码如下(PS:当然在这里没有什么必要,可以省略):

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
      * 字符串XSS过滤,JavaScript过滤,Sql过滤
     
      * @param str 传入的字符串
     
      * @return 转义后的字符串
      * */
     public  static  String xssEncode(String str){
         String s = StringEscapeUtils.escapeHtml(str);
//      s = StringEscapeUtils.escapeJavaScript(s);
//      s = StringEscapeUtils.escapeSql(s);      
         return  s; 
     }




本文转自 pangfc 51CTO博客,原文链接:http://blog.51cto.com/983836259/1706029,如需转载请自行联系原作者
相关文章
|
26天前
|
Java
新闻发布项目——后台JSP界面adminManage/addNews.jsp
新闻发布项目——后台JSP界面adminManage/addNews.jsp
12 0
|
26天前
|
Java
新闻发布项目——后台JSP界面adminManage/editNews.jsp
新闻发布项目——后台JSP界面adminManage/editNews.jsp
11 0
|
26天前
|
Java
新闻发布项目——后台JSP界面adminManage/manageTopic.jsp
新闻发布项目——后台JSP界面adminManage/manageTopic.jsp
9 0
|
26天前
|
Java
新闻发布项目——后台JSP界面adminManage/modifyCategory.jsp
新闻发布项目——后台JSP界面adminManage/modifyCategory.jsp
8 0
|
30天前
|
Java 数据安全/隐私保护
jsp使用cookie实现记住用户名和密码
jsp使用cookie实现记住用户名和密码
11 0
|
30天前
|
Java
java实现动态验证码源代码——绘制验证码的jsp
java实现动态验证码源代码——绘制验证码的jsp
13 0
|
30天前
|
前端开发 Java
java实现动态验证码源代码——jsp页面
java实现动态验证码源代码——jsp页面
11 0
|
3月前
|
Java 数据库 数据安全/隐私保护
基于SSM框架实现管科类考研自我管理系统(分前后台spring+springmvc+mybatis+maven+jsp+jquery)
基于SSM框架实现管科类考研自我管理系统(分前后台spring+springmvc+mybatis+maven+jsp+jquery)
|
8月前
|
JSON 小程序 前端开发
小程序轮播图的两种后台方式(JSP)--【浅入深出系列009】
小程序轮播图的两种后台方式(JSP)--【浅入深出系列009】
|
28天前
|
Java
新闻发布项目——后台JSP界面adminManage/newsPage.jsp
新闻发布项目——后台JSP界面adminManage/newsPage.jsp
9 0