只需几步就可以生成动态随机的验证码,最终效果如下图:
一 前台显示页面login.jsp
其中验证码显示的是一张图片,链接指向的是生成验证码的servlet,同时点击图片后触发changeImg()这个js函数,使其动态生成一个新的验证码,这个函数中的参数t=Math.random()并不会参与验证码的生成,它的作用仅仅只是表示每次提交的并不是同一个请求,需要单独处理,完整的login.jsp代码如下:
<%@ 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
新增一个节点,代码如下:
<!-- 验证码绘制 --> <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完整代码如下:
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相关代码如下:
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()目的是将一个字符串中的小写字母转换为大写字母,代码如下:
/** * 将一个字符串中的小写字母转换为大写字母 * * */ 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:当然在这里没有什么必要,可以省略):
/** * 字符串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; }
本文出自 “zifangsky” 博客,请务必保留此出处http://983836259.blog.51cto.com/7311475/1706029
在JSP中动态生成随机验证码,登录时后台校验验证码,以及如何避免同一个验证码被重复提交爆破密码
原文地址:http://983836259.blog.51cto.com/7311475/1706029