服务器之家

服务器之家 > 正文

Java Web项目中验证码功能的制作攻略

时间:2020-05-05 14:36     来源/作者:夜空中苦逼的程序员

一、前言
在表单页面中使用验证码的好处在于有效防止用户恶意提交表单,或者使用外挂非法攻击系统。

二、准备条件
1、一个普通的web项目 webProject;
2、一个web服务器 Tomcat。

三、实现思路:
1、自定义一个servlet  VerifyCodeServlet 画一个包含验证字符的验证码图片,这里的图片需要使用Graphics2D手动去画;
2、在具体页面使用img标签的src引用这个servlet即可显示servlet;
3、因为画图的时候把验证码信息放入了session,所以提交表单后可以根据session中保存的值和用户输入的code做比较,验证输入是否正确。
网上大都是通过servlet实现的验证码,入下图逻辑:

Java Web项目中验证码功能的制作攻略

步骤:
1、请求登录页面时随机生成验证码字符串;
2、将生成对验证码字符串存到session中;
3、根据验证码字符串生成验证码图片,然后将验证码图片输出到客户展示;
4、提交登录请求时用户输入的验证码字符串与session中的字符串做比对。

四、具体代码如下:

?
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package com.servlet;
 
import java.awt.Color;  
import java.awt.Font;  
import java.awt.Graphics2D;  
import java.awt.image.BufferedImage;  
import java.util.Random;  
   
import javax.imageio.ImageIO;  
import javax.servlet.ServletException;  
import javax.servlet.ServletOutputStream;  
import javax.servlet.http.HttpServlet;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
import javax.servlet.http.HttpSession;  
   
/**
 * 产生验证码图片的servlet
 * @author Administrator
 *
 */
public class VerifyCodeServlet extends HttpServlet {  
    
  private static final long serialVersionUID = -5051097528828603895L;
  
  /**
  * 验证码图片的宽度。
  */
  private int width = 100;  
    
  /**
  * 验证码图片的高度。
  */
  private int height = 30;  
   
  /**
  * 验证码字符个数
  */
  private int codeCount = 4;  
   
  /**
  * 字体高度 
  */
  private int fontHeight;  
   
  /**
  * 第一个字符的x轴值,因为后面的字符坐标依次递增,所以它们的x轴值是codeX的倍数
  */
  private int codeX;  
   
  /**
  * codeY ,验证字符的y轴值,因为并行所以值一样
  */
  private int codeY;  
   
  /**
  * codeSequence 表示字符允许出现的序列值
  */
  char[] codeSequence = { '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };  
   
  /** 
   * 初始化验证图片属性 
   */
  public void init() throws ServletException {  
    // 从web.xml中获取初始信息  
    // 宽度  
    String strWidth = this.getInitParameter("width");  
    // 高度  
    String strHeight = this.getInitParameter("height");  
    // 字符个数  
    String strCodeCount = this.getInitParameter("codeCount");  
    // 将配置的信息转换成数值  
    try {  
      if (strWidth != null && strWidth.length() != 0) {  
        width = Integer.parseInt(strWidth);  
      }  
      if (strHeight != null && strHeight.length() != 0) {  
        height = Integer.parseInt(strHeight);  
      }  
      if (strCodeCount != null && strCodeCount.length() != 0) {  
        codeCount = Integer.parseInt(strCodeCount);  
      }  
    } catch (NumberFormatException e) {
      e.printStackTrace();
    }  
    //width-4 除去左右多余的位置,使验证码更加集中显示,减得越多越集中。
    //codeCount+1   //等比分配显示的宽度,包括左右两边的空格
    codeX = (width-4) / (codeCount+1);
    //height - 10 集中显示验证码
    fontHeight = height - 10
    codeY = height - 7
  }  
   
  /**
  * @param request
  * @param response
  * @throws ServletException
  * @throws java.io.IOException
  */
  protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException {  
    // 定义图像buffer  
    BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);  
    Graphics2D gd = buffImg.createGraphics();  
    // 创建一个随机数生成器类  
    Random random = new Random();  
    // 将图像填充为白色  
    gd.setColor(Color.LIGHT_GRAY);  
    gd.fillRect(0, 0, width, height);  
    // 创建字体,字体的大小应该根据图片的高度来定。  
    Font font = new Font("Fixedsys", Font.PLAIN, fontHeight);  
    // 设置字体。  
    gd.setFont(font);  
    // 画边框。  
    gd.setColor(Color.BLACK);  
    gd.drawRect(0, 0, width - 1, height - 1);  
    // 随机产生160条干扰线,使图象中的认证码不易被其它程序探测到。  
    gd.setColor(Color.gray);  
    for (int i = 0; i < 16; i++) {  
      int x = random.nextInt(width);  
      int y = random.nextInt(height);  
      int xl = random.nextInt(12);  
      int yl = random.nextInt(12);  
      gd.drawLine(x, y, x + xl, y + yl);  
    }  
    // randomCode用于保存随机产生的验证码,以便用户登录后进行验证。  
    StringBuffer randomCode = new StringBuffer();  
    int red = 0, green = 0, blue = 0;  
    // 随机产生codeCount数字的验证码。  
    for (int i = 0; i < codeCount; i++) {  
      // 得到随机产生的验证码数字。  
      String strRand = String.valueOf(codeSequence[random.nextInt(36)]);  
      // 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同。  
      red = random.nextInt(255);  
      green = random.nextInt(255);  
      blue = random.nextInt(255);  
      // 用随机产生的颜色将验证码绘制到图像中。  
      gd.setColor(new Color(red,green,blue));  
      gd.drawString(strRand, (i + 1) * codeX, codeY);  
      // 将产生的四个随机数组合在一起。  
      randomCode.append(strRand);  
    }  
    // 将四位数字的验证码保存到Session中。  
    HttpSession session = request.getSession();  
    session.setAttribute("validateCode", randomCode.toString());
    // 禁止图像缓存。  
    response.setHeader("Pragma", "no-cache");  
    response.setHeader("Cache-Control", "no-cache");  
    response.setDateHeader("Expires", 0);  
   
    response.setContentType("image/jpeg");  
    // 将图像输出到Servlet输出流中。  
    ServletOutputStream sos = response.getOutputStream();  
    ImageIO.write(buffImg, "jpeg", sos);
    sos.close();  
  }  

然后在web.xml中配置下这个生成验证码的servlet,具体如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<servlet>
  <servlet-name>VerifyCodeServlet</servlet-name>
  <servlet-class>com.servlet.VerifyCodeServlet</servlet-class>
  <init-param>
    <param-name>width</param-name>
    <param-value>120</param-value>
  </init-param>
  <init-param>
    <param-name>height</param-name>
    <param-value>32</param-value>
  </init-param>
  <init-param>
    <param-name>codeCount</param-name>
    <param-value>4</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>VerifyCodeServlet</servlet-name>
  <url-pattern>/VerifyCodeServlet</url-pattern>
</servlet-mapping>

启动服务器,在浏览器地址栏输入:http://localhost:8080/webProject/VerifyCodeServlet
查看显示效果,具体如下:

1、 每次刷新验证码都有变化,这是因为servlet设置了禁用浏览器缓存。
2、这里发现一个问题:如果使用火狐浏览器,VerifyCodeServlet中重写的serviice方法被执行了两次,换成重写doGet方法或者doPost方法也是一样, 并且其他浏览器都不见该情况,后来发现如果是通过页面引用该servlet就调用正常了。

然后就可以在页面中引用该验证码了,具体代码如下:

?
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"%>
<!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=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<div>
<%
  String inputCode = request.getParameter("inputCode");
  String verifyCode = (String)session.getAttribute("validateCode");
  if(inputCode!=null && verifyCode!=null){
    out.print("真正的验证码:" + verifyCode + "<br/>" + "用户输入的验证码:" + inputCode + "<br/>");
    inputCode = inputCode.toUpperCase();//不区分大小写
    out.print("比较验证码证明用户输入 " + (inputCode.equals(verifyCode)?"正确":"错误") + "!");
  }
%>
<form action="index.jsp">
验证码:<input name="inputCode" value=""/> 
<img src="VerifyCodeServlet" align="middle" title="看不清,请点我" onclick="javascript:refresh(this);" onmouseover="mouseover(this)"/><br/>
<input name="submit" type="submit" value="提交"/>
</form>
</div>
<script>
  function refresh(obj){
     obj.src = "VerifyCodeServlet?" + Math.random();
  }
   
  function mouseover(obj){
    obj.style.cursor = "pointer";
  }
</script>
</body>
</html>

上面代码通过表单提交验证码到当前jsp,验证用户输入的验证码是否正确,运行的具体效果如下:
1、输入正确的验证码

Java Web项目中验证码功能的制作攻略

2、输入错误的验证码

Java Web项目中验证码功能的制作攻略

标签:

相关文章

热门资讯

沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意
沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意 2019-07-07
玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分
玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分 2019-06-21
男生常说24816是什么意思?女生说13579是什么意思?
男生常说24816是什么意思?女生说13579是什么意思? 2019-09-17
超A是什么意思 你好a表达的是什么
超A是什么意思 你好a表达的是什么 2019-06-06
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享 2020-04-07
返回顶部