最近在学用JAVA Servlet/JSP来开发web应用,于是练手来做一个通过Servlet后台来生成图片验证码,并实现验证功能。
实现思路
先用单例模式写一个生成验证码图片的GenerateCode类,该类的createImage()方法返回BufferImage类,然后用Servlet来读取图片,GenerateCode对象在生成图片的同时也会生成一个验证码的字符串,可供后台验证的Servlet来验证前台post上去的验证码是否相同.
实现代码
Servlet类
- VerifyCode.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
40package servlet;
import Utils.GenerateCode;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
“/VerifyCode.do”) (
public class VerifyCode extends HttpServlet {
private void processRequest(HttpServletRequest req, HttpServletResponse resp) {
resp.setContentType(“image/jpg”);
GenerateCode generateCode = GenerateCode.getInstance();
try {
OutputStream outputStream = resp.getOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(outputStream);
encoder.encode(generateCode.createImage());
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
processRequest(req, resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
processRequest(req, resp);
}
} - Verify.java(验证post上来的验证码)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package servlet;
import Utils.GenerateCode;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
“/Verify.do”) (
public class Verify extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType(“text/html;charset=UTF-8”);
String reallycode = GenerateCode.getInstance().getCode();
String postcode = req.getParameter(“verifycode”);
if(reallycode.equalsIgnoreCase(postcode)){
req.getRequestDispatcher(“success.jsp”).forward(req,resp);
}else
resp.sendRedirect(“index.jsp”);
}
}
Utils
- GenerateCode.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
70package Utils;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
public class GenerateCode{
private static final int PW = 70;
private static final int PH = 35;
private static Random random = new Random();
private static String[] fontnames = {“Chalkboard”,“Cochin”,“Skia”,“Apple Symbols”};
private String codes=“012345678901234567890123456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ”;
private static Color bgColor = Color.BLACK;
private String code;
private volatile static GenerateCode generateCode;
public static GenerateCode getInstance(){
if(generateCode ==null){
synchronized (GenerateCode.class){
if(generateCode ==null){
generateCode = new GenerateCode();
}
}
}
return generateCode;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
private Color randomColor(){
return new Color(random.nextInt(150),random.nextInt(150),random.nextInt(150));
}
private Font randomFont(){
int index = random.nextInt(fontnames.length);
String fontname = fontnames[index];
int style = random.nextInt(4);
int size = random.nextInt(5) + 25;
return new Font(fontname,style,size);
}
private String randomCode(){
int len = codes.length();
String code = “”;
for(int i=0;i<4;i++){
int a = random.nextInt(len);
code += codes.charAt(a);
}
return code;
}
public BufferedImage createImage(){
BufferedImage image = new BufferedImage(PW,PH,BufferedImage.TYPE_INT_BGR);
Graphics g = image.getGraphics();
g.setColor(bgColor);
g.fillRect(0,0,PW,PH);
String num = randomCode();
this.setCode(num);
g.setColor(randomColor());
g.setFont(randomFont());
g.drawString(num,0,25);
return image;
}
}
jsp
index.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<%@ page import=“java.awt.image.BufferedImage” %>
<%@ page import=“servlet.VerifyCode” %><%–
Created by IntelliJ IDEA.
User: onlyless
Date: 11/17/18
Time: 11:34
To change this template use File | Settings | File Templates.
–%>
<%@ page contentType=“text/html;charset=UTF-8” language=“java” %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action=“/Verify.do” method=“post”>
<table>
<tr>
<td>
<img src=“/VerifyCode.do”>
<input type=“text” name=“verifycode” maxlength=“4”>
<input type=“submit” name=“submit”>
</td>
</tr>
</table>
</form>
</body>
</html>success.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<%–
Created by IntelliJ IDEA.
User: onlyless
Date: 11/16/18
Time: 16:46
To change this template use File | Settings | File Templates.
–%>
<%@ page contentType=“text/html;charset=UTF-8” language=“java” %>
<html>
<head>
<title>success</title>
</head>
<body>
<h1>success</h1>
</body>
</html>
不足
思路很简单,最终也实现出来了,但是我发现了一个BUG,在多个窗口同时请求验证时,由于后台很简单的读取GenerateCode的验证码,因此这多个窗口post上来的验证码就算全部都是正确的,但是只能验证最后生成验证码图片的那个post,要解决这个问题,我想应该要用到Cookie的知识,现在还在学习,待以后再解决这个问题。
后台查看验证码不一致,后台正确的验证码是最后生成的验证码,导致之前的生成的验证码提交上去不匹配
后续
第二天学到了Cookie和Session的运用,发现用Session很简单就能解决上面的那个不足。
- VerifyCode.java
1
2
3
4
5
6…
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(outputStream);
encoder.encode(generateCode.createImage());
req.getSession().setAttribute(“verify”,generateCode.getCode()); //添加的代码
outputStream.close();
… - Verify.java
1
2
3
4
5
6
7…
resp.setContentType(“text/html;charset=UTF-8”);
// String reallycode = GenerateCode.getInstance().getCode();
String reallycode = (String) req.getSession().getAttribute(“verify”); //修改的代码
String postcode = req.getParameter(“verifycode”);
System.out.println(reallycode + “—“ + postcode);
….
两个浏览器同时登陆都成功登陆,后台查看验证码一致
不过我发现这个问题在同一个浏览器上还是存在,原因很简单,用一个浏览器不同窗口的Session都是一样的,这样就导致新生成的验证码覆盖掉以前Session绑定的验证码,导致问题产生,这个问题我测试了一下学校的教务系统和12306的登录验证码,都存在这样的问题。不过这个一般谁也不会同时开多个窗口登录,登录失败一个,换以前的窗口登录,这样导致一直匹配到的不是产生最新的验证码。