[带菜鸟飞]验证码功能实现

验证码一般用于登录功能,其实现一般分为两种,js验证码生成和服务器验证码生成。

js的生成也就是使用js的2D功能画出验证码,这种方法实现起来只要调用几个js插件就可以快速生成,但缺点是,如果有人通过更改浏览器js来绕过验证,这验证码的防线就是一种摆设了。

 

至于服务器的验证码生成要安全的多。其原理也不复杂。

1.在打开登录页面时,通过加载图片元素标签img的src属性可再发出一个请求到后台。

2.在这个请求中可以在服务器中生成一个随机验证字符串并放置于session中。

3.利用java中的2d功能把字符串生成一个图片二进制,并在图片中加入杂波和着色等等。

4.请求返回图片流输出时,把生成后的图片二进制流输出即可。

5.登录验证时,将请求参数中的验证码与session中保存的验证时进行比较验证,无论验证成功与否均在方法最后清除session中的验证码。

 

开始编码,就以上面的步骤来一步步的写

1.先写html页面

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>    
    <title>token</title>
    <script type="text/javascript">
      function changeImage(imageComponent){
        var time = new Date().getTime();
      	imageComponent.src = "${pageContext.request.contextPath}/characters.jpg?" + time;
      }
    </script>
 </head>
  
  <body>
  	<form method="post" action="next.jsp">
  		username:<input type="text" name="username" />
  		password:<input type="password" name="password" />
  		<input type="text" name="token" />
  		<input type="submit" value="login" />
    <img οnclick="changeImage(this)" src="${pageContext.request.contextPath}/characters.jpg" />
    </form>
  </body>
</html>

 

 这里可以看到,在这个页面中有三个输入框,一个用户名,一个密码,还有一个验证码。最后还有一个图片标签。地址就是要生成验证码图片的请求。

加入了一个js方法,就是在点击验证码图片时可以换一个验证码。

这个不用讲,没什么好说的。

 

2.要处理请求就要先给个请求的处理机制。这里用xml配个servlet来解决

 

	<servlet>
		<servlet-name>ImageTokenServlet</servlet-name>
		<servlet-class>guides.servlet.imgtoken.ImageTokenServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>ImageTokenServlet</servlet-name>
		<url-pattern>/characters.jpg</url-pattern>
	</servlet-mapping>

 

 

再来看看ImageTokenServlet是怎么来处理的

 

public class ImageTokenServlet extends HttpServlet {
    private int width = 80;
    private int height = 30;
    private String characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private int length = 4;
    
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        //产生tokens并将其保存在当前的会话中        
        TokenUtil.saveToken(request, characters, length);

        //生成图片响应
        TokenUtil.generateTokenImage(response, request.getSession(), width, height);
    }
   
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        doGet(request, response);
    }
    
    }
}

  这里可以看出,servlet也只做了两件事,就是调用了两个工具类方法。至于最上面的属性从名子上就能看出他们的含义了,不解释。

 

再来看TokenUtil.saveToken(request, characters, length);的实现

 

 public static void saveToken(HttpServletRequest request, String characters, int length) {
	Random ran = new Random();
        char[] tokens = new char[length];
        for (int i = 0; i < length; i++) {
            char ch = characters.charAt(ran.nextInt(characters.length()));
            tokens[i] = ch;
        }

        HttpSession session = request.getSession();
        session.setAttribute("token", new String(tokens));
    }
 

 

到目前为止,随机的验证码已经生成了

 

3.看看是怎么把字符串生成一个图片二进制流的

 

 public static void generateTokenImage(HttpServletResponse response, HttpSession session, int width, int height) throws IOException {
        //设置响应内容为图片格式
        response.setContentType("image/jpeg");

        //禁止浏览器缓存
        response.addHeader("pragma", "NO-cache");
		response.addHeader("Cache-Control", "no-cache");
		response.addDateHeader("Expries", 0);

        //绘制图片
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();

        //以下填充背景颜色
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, width, height);

        //设置字体颜色
        g.setColor(Color.RED);
        Font font = new Font("Arial", Font.BOLD, 18);
        g.setFont(font);

        //绘制
        String token = (String) session.getAttribute("token");
        g.drawString(token, 5, height - 2);
        g.dispose();

        //发送内容到客户端
        ServletOutputStream out = response.getOutputStream();
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
        encoder.encode(image);
        out.close();
    }

 

 注意,这里不仅是把图片生成了,也同时使用了response的输出流把图片给输出了。

 

4.输入功能已经在上一步给完成了。。。

 

5.登录验证时,再写一个工具方法即可

 

public static boolean isTokenValid(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            return false;
        }

        boolean isLegal = true;
        String[] tokenParams = (String[]) request.getParameterValues("token");
        if (tokenParams == null) {
            isLegal = false;
        } else {
            String token = tokenParams[0];
            if (token == null) {
                isLegal = false;
            } else {
                String sessionToken = (String) session.getAttribute("token");
                if (!token.equalsIgnoreCase(sessionToken)) {
                    isLegal = false;
                }
            }
        }
        //删除会话中的token信息
        session.removeAttribute("token");

        return isLegal;
    }

 这个工具类方法在登录验证时调用一下查看一下返回值即可

 

OK了,整个流程的核心就是这么多。

 

把servlet类和工具类再整理一下,完整点重新贴出来。

 

package guides.servlet.imgtoken;

import guides.servlet.util.TokenUtil;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 用来以图片的形式来生成验证码。
 * 生成验证码后先将其保存到用户的当前会话中,然后在以图片的形式发送到客户端。
 * 验证码的验证要通过TokenFilter来完成。
 * 该Servlet包含如下的初始化参数,来定制验证码的信息:
 * <ul>
 * <li>characters:生成验证码所有的字符序列,默认为数字和字母</li>
 * <li>length:生成的验证码的长度,默认为4位</li>
 * <li>width:验证码图片的显示宽度,默认为80像素</li>
 * <li>height:验证码图片的高度,默认为30像素</li>
 * </ul>
 * 
 * 该Servlet需要在应用的web.xml进行如下部署:
 * <servlet>
 *   <servlet-name>ImageTokenServlet</servlet-name>
 *   <servlet-class>guides.servlet.imgtoken.ImageTokenServlet</servlet-class>
 * </servlet>
 * <servlet-mapping>
 *   <servlet-name>ImageTokenServlet</servlet-name>
 *   <url-pattern>/token/image.jpg</url-pattern>
 * </servlet-mapping>
 * 
 * 然后在客户端网页中进行如下引用:
 * <img src="/yourContextPath/token/image.jpg"></img>
 */
@SuppressWarnings("serial")
public class ImageTokenServlet extends HttpServlet {
    private int width = 80;
    private int height = 30;
    private String characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private int length = 4;
    
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        //产生tokens并将其保存在当前的会话中        
        TokenUtil.saveToken(request, characters, length);

        //生成图片响应
        TokenUtil.generateTokenImage(response, request.getSession(), width, height);
    }
   
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        doGet(request, response);
    }
    
    public void init() {
        String initParam = getInitParameter("characters");
        if (initParam != null) {
            this.characters = initParam;
        }

        initParam = getInitParameter("length");
        if (initParam != null) {
            this.length = Integer.parseInt(initParam);
        }

        initParam = getInitParameter("width");
        if (initParam != null) {
            this.width = Integer.parseInt(initParam);
        }

        initParam = getInitParameter("height");
        if (initParam != null) {
            this.height = Integer.parseInt(initParam);
        }
    }
}

 

 

 

package guides.servlet.util;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

/**
 * 验证码处理工具类,包含如下功能:
 * 1)产生验证码
 * 2)保存验证码
 * 3)校验验证码
 * 4)生成图像验证码
 */
public class TokenUtil {
	public static final String TOKEN_KEY_NAME = "token";
	public static final String TOKEN_PARAMETER_NAME = "token";
    /**
     * 根据给定的字符序列生成随即的验证码。
     * 
     * @param characters 验证码的字符取值范围
     * @param length 生成的验证码的长度
     * @return 指定长度的验证码字符串
     */
    public static String generateToken(String characters, int length) {
        Random ran = new Random();
        char[] tokens = new char[length];
        for (int i = 0; i < length; i++) {
            char ch = characters.charAt(ran.nextInt(characters.length()));
            tokens[i] = ch;
        }

        return new String(tokens);
    }
   
    /**
     * 生成验证码,并将其保存到当前的会话中。该方法主要应用于Struts1,Servlet,JSP环境。
     * 如果会话不存在,自动创建会话。
     * 
     * @param session 当前的请求对象。
     * @param characters 验证码的字符取值范围
     * @param length 生成的验证码的长度
     */
    public static void saveToken(HttpServletRequest request, String characters, int length) {
        HttpSession session = request.getSession();
        session.setAttribute(TOKEN_KEY_NAME, generateToken(characters, length));
    }
  

    /**
     * 针对当前的请求进行验证码的校验,并且在校验完毕候从会话中清空验证码,将其作废。
     * 该方法适用于Struts1,Servlet,JSP环境。
     * 
     * 下面的情况标识校验通过:
     * 1)请求中包含名字为TokenConstants.TOKEN_PARAMETER_NAME所指定的参数。
     * 2)会话中包含名字为TokenConstants.TOKEN_KEY_NAME所指定的属性。
     * 3)1参数的值和2属性的值(字符串类型)相等(忽略大小写)。
     * 
     * @param request HTTP请求对象。
     * @return 验证通过返回true,否则返回false。
     */
    public static boolean isTokenValid(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            return false;
        }

        boolean isLegal = true;
        String[] tokenParams = (String[]) request.getParameterValues(TOKEN_PARAMETER_NAME);
        if (tokenParams == null) {
            isLegal = false;
        } else {
            String token = tokenParams[0];
            if (token == null) {
                isLegal = false;
            } else {
                String sessionToken = (String) session.getAttribute(TOKEN_KEY_NAME);
                if (!token.equalsIgnoreCase(sessionToken)) {
                    isLegal = false;
                }
            }
        }
        //删除会话中的token信息
        session.removeAttribute(TOKEN_KEY_NAME);

        return isLegal;
    }

    /**
     * 向客户端生成验证码图片。
     * 
     * @param response HHTP 响应对象。
     * @param session 包含会话属性信息的Map对象。
     * @param width 验证码图片的宽度。
     * @param height 验证码图片的高度。
     * @throws IOException 执行操作时发生网络错误时。
     */
    public static void generateTokenImage(HttpServletResponse response, HttpSession session, int width, int height) throws IOException {
        //设置响应内容为图片格式
        response.setContentType("image/jpeg");

        //禁止浏览器缓存
        response.addHeader("pragma", "NO-cache");
		response.addHeader("Cache-Control", "no-cache");
		response.addDateHeader("Expries", 0);

        //绘制图片
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();

        //以下填充背景颜色
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, width, height);

        //设置字体颜色
        g.setColor(Color.RED);
        Font font = new Font("Arial", Font.BOLD, 18);
        g.setFont(font);

        //绘制
        String token = (String) session.getAttribute(TOKEN_KEY_NAME);
        g.drawString(token, 5, height - 2);
        g.dispose();

        //发送内容到客户端
        ServletOutputStream out = response.getOutputStream();
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
        encoder.encode(image);
        out.close();
    }
}
 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值