在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{