实现形如下图的验证码:
编写一个验证码生成类,首先写一个产生随机字符的方法:
public class CaptchaCode {
private static char randomChar(){
String string="QWERTYUIOPASDFGHJKLZXCVBNM1234567890";
Random random=new Random();
return string.charAt(random.nextInt(string.length()));
}
}
然后写一个生成验证码图片的方法:
public static String drawImage(HttpServletResponse response){
//1.拼接字符串的类
StringBuilder builder=new StringBuilder();
//产生4位验证码
for (int i = 0; i <4 ; i++) {
builder.append(randomChar());
}
String code=builder.toString();
//2.定义图片的宽度和高度
int width=120;
int height=25;
//建立BufferedImage对象,指定图片缓冲流的长宽度和样式
BufferedImage bi=new BufferedImage(width,height,BufferedImage.TYPE_3BYTE_BGR);
//3.获取Graphics2d绘制对象,开始绘制验证码。图片最终会注入到缓冲流中
Graphics2D graph=bi.createGraphics();
//4.设置字体和大小
Font font=new Font("宋体",Font.PLAIN,20);
Color color=new Color(0,255,255);
graph.setFont(font);
graph.setColor(color);
graph.setBackground(new Color(0,0,0));
//绘制形状,一般是矩形
graph.clearRect(0,0,width,height);
//创建字体对象
FontRenderContext context=graph.getFontRenderContext();
//获得文字的边界
Rectangle2D bounds=font.getStringBounds(code,context);
//计算坐标和间距,以便字体放在矩形的正中央
double x=(width-bounds.getWidth())/2;
double y=(height-bounds.getHeight())/2;
double acsent=bounds.getY();
double baseY=y-acsent;
//在矩形中绘制字体
graph.drawString(code,(int)x,(int)baseY);
graph.dispose();
//保存到响应的输出流
try {
ImageIO.write(bi,"jpg",response.getOutputStream());
//刷新响应流
response.flushBuffer();
} catch (IOException e) {
e.printStackTrace();
}
//用于验证码的对比和存储
return code;
}
编写code.jsp,调用上面的方法,在响应中添加一个验证码图片流。
<%
//清空浏览器缓存
response.setHeader("pragma","no-cache");
response.setHeader("cache-control","no-cache");
response.setHeader("expires","0");
//获取验证码字符,用于比对
String code=CaptchaCode.drawImage(response);
session.setAttribute("code",code);
out.clear();
out=pageContext.pushBody();
%>
在主页中,令img标签的源为上面的code.jsp,同时添加一个超链接,当点击超链接后,将img标签的src重新设置,每次点击都传入一个不同的请求,用于更换验证码。
看不清?换一张
实现如下形式的验证码:
新建一个获取随机背景色的方法
private static Color getRandomColor(int fc,int bc){
Random random=new Random();
if(fc>255) fc=255;
if(bc>255) bc=255;
int r=fc+random.nextInt(bc-fc);
int g=fc+random.nextInt(bc-fc);
int b=fc+random.nextInt(bc-fc);
return new Color(r,g,b);
}
编写产生算术验证码方法,与之前的类似,只不过需要绘制干扰线。
public static String drawImageVerificate(HttpServletResponse response){
int width=100,height=30;
BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_BGR);
Graphics2D graph=image.createGraphics();
Random random=new Random();
//设置背景
graph.setColor(getRandomColor(240,250));
graph.setFont(new Font("微软雅黑",Font.PLAIN,22));
graph.fillRect(0,0,width,height);
//设置干扰线颜色并绘制线条在图片中
graph.setColor(getRandomColor(180,230));
//100条
for (int i = 0; i <100 ; i++) {
int x=random.nextInt(width);
int y=random.nextInt(height);
int x1=random.nextInt(60);
int y1=random.nextInt(60);
graph.drawLine(x,y,x1,y1);
}
//算术表达式拼接
int num1=(int)(Math.random()*10+1);
int num2=(int)(Math.random()*10+1);
int optor=random.nextInt(3);
int result=0;
String optorstr="";
switch (optor){
case 0 :optorstr="+";result=num1+num2;break;
case 1 :optorstr="-";result=num1-num2;break;
case 2 :optorstr="*";result=num1*num2;break;
}
//拼接算术表达式
String calc=num1+" "+optorstr+" "+num2+" = ? ";
//设置表达式颜色
graph.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
//绘制表达式
graph.drawString(calc,5,25);
graph.dispose();
try {
ImageIO.write(image,"JPEG",response.getOutputStream());
return String.valueOf(result);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
code.jsp和index.jsp与之前一样。
使用框架来产生验证码十分方便和快捷,可以指定各种样式。这里使用了Kaptcha框架,首先需要导入相关jar包,然后在web.xml中进行配置。
第一行比较重要,指定了生成验证码图片的地址为/kaptcha.jpg。
Kaptcha
/kaptcha.jpg
Kaptcha
com.google.code.kaptcha.servlet.KaptchaServlet
kaptcha.border
no
kaptcha.image.width
100
kaptcha.image.height
40
kaptcha.textproducer.font.size
28
kaptcha.textproducer.char.string
qwertyuiopasdfghjklzxcvbnm123456789
kaptcha.textproducer.char.length
4
kaptcha.noise.impl
com.google.code.kaptcha.impl.DefaultNoise
kaptcha.obscurificator.impl
com.google.code.kaptcha.impl.FishEyeGimpy
kaptcha.session.key
kcode
最后需要将生成的验证码,通过kaptcha.session.key来保存,通过kcode来访问生存的验证码字符串。
首先编写一个用于测试的jsp。
然后编写一个js脚本,当用于点击图片的时候,产生一个新的验证码:
然后再编写一个js脚本,绑定“登录”按键。
最后编写url为login的Servlet:
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("UTF-8");
//获取浏览器输出流对象,用于输出到ajax
PrintWriter out = response.getWriter();
//获取用户传递过来的验证码
String code = request.getParameter("code");
//获取验证码框架产生的验证码(会话中存储的验证码)
String sessionCode = (String)request.getSession().getAttribute("kcode");
if(code!=null&sessionCode!=null) {
//如果用户输入的验证码和产生在服务器端的验证码一致,那么就告诉用户输入正确
if (code.equalsIgnoreCase(sessionCode)) {
//登录逻辑、注册逻辑等相关的业务操作
out.print("success");
} else {
out.print("fail");
}
}
out.flush();
out.close();
}
这样就完成了验证码的校验