首页
技术知识库
Task工作计划
网站简介
DON框架
后台管理
文章分类
JAVA
框架知识
操作系统
容器相关
数据库层
优化技术
界面编程
网络编程
开发工具
GO语言
其他
读书随笔
观影随笔
每日随笔
APP
如何验证码功能[验证]
所属分类
:[网络编程] |
创建时间
:2013-01-13 |
文章属性
:原创 |
文章来源
:http://windfly.cn |
作者
:windfly
<p> 验证码一般用于登录功能,其实现一般分为两种,js验证码生成和服务器验证码生成。 </p> <p> js的生成也就是使用js的2D功能画出验证码,这种方法实现起来只要调用几个js插件就可以快速生成,但缺点是,如果有人通过更改浏览器js来绕过验证,这验证码的防线就是一种摆设了。 </p> <p>   </p> <p> 至于服务器的验证码生成要安全的多。其原理也不复杂。 </p> <p> 1.在打开登录页面时,通过加载图片元素标签img的src属性可再发出一个请求到后台。 </p> <p> 2.在这个请求中可以在服务器中生成一个随机验证字符串并放置于session中。 </p> <p> 3.利用java中的2d功能把字符串生成一个图片二进制,并在图片中加入杂波和着色等等。 </p> <p> 4.请求返回图片流输出时,把生成后的图片二进制流输出即可。 </p> <p> 5.登录验证时,将请求参数中的验证码与session中保存的验证时进行比较验证,无论验证成功与否均在方法最后清除session中的验证码。 </p> <p>   </p> <p> 开始编码,就以上面的步骤来一步步的写 </p> <p> 1.先写html页面 </p> <p>   </p> <pre name="code" class="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 onclick="changeImage(this)" src="${pageContext.request.contextPath}/characters.jpg" /> </form> </body> </html></pre> <p>   </p> <p>  这里可以看到,在这个页面中有三个输入框,一个用户名,一个密码,还有一个验证码。最后还有一个图片标签。地址就是要生成验证码图片的请求。 </p> <p> 加入了一个js方法,就是在点击验证码图片时可以换一个验证码。 </p> <p> 这个不用讲,没什么好说的。 </p> <p>   </p> <p> 2.要处理请求就要先给个请求的处理机制。这里用xml配个servlet来解决 </p> <p>   </p> <pre name="code" class="xml"><span style="white-space: pre"> </span><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></pre> <p>   </p> <p>   </p> <p> 再来看看ImageTokenServlet是怎么来处理的 </p> <p>   </p> <pre name="code" class="java">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); } } }</pre> <p>   这里可以看出,servlet也只做了两件事,就是调用了两个工具类方法。至于最上面的属性从名子上就能看出他们的含义了,不解释。 </p> <p>   </p> <p> 再来看TokenUtil.saveToken(request, characters, length);的实现 </p> <p>   </p> <pre name="code" class="java"> public static void saveToken(HttpServletRequest request, String characters, int length) { <span style="white-space: pre"> </span>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)); }</pre>   <p>   </p> <p> 到目前为止,随机的验证码已经生成了 </p> <p>   </p> <p> 3.看看是怎么把字符串生成一个图片二进制流的 </p> <p>   </p> <pre name="code" class="java"> 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(); }</pre> <p>   </p> <p>  注意,这里不仅是把图片生成了,也同时使用了response的输出流把图片给输出了。 </p> <p>   </p> <p> 4.输入功能已经在上一步给完成了。。。 </p> <p>   </p> <p> 5.登录验证时,再写一个工具方法即可 </p> <p>   </p> <pre name="code" class="java">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; }</pre> <p>  这个工具类方法在登录验证时调用一下查看一下返回值即可 </p> <p>   </p> <p> OK了,整个流程的核心就是这么多。 </p> <p>   </p> <p> 把servlet类和工具类再整理一下,完整点重新贴出来。 </p> <p>   </p> <pre name="code" class="java">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); } } }</pre> <p>   </p> <p>   </p> <p>   </p> <pre name="code" class="java">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(); } }</pre>   <p>   </p> <p>   </p>
返回