servlet+jsp实现验证码

今天是2018年9月6日

使用Servlet+Jsp完成验证码主要有如下步骤

  • 完成HTML页面的编写
  • 完成生成随机验证码的Servlet
  • 完成刷新更换验证码的功能
  • 完成验证验证码是否正确是Servlet

1.Html的编写

页面结构相对比较简单,主要有input-text,img,a三种元素组成,input-text用于用户输入验证码,img用于显示验证码,而通常页面中都会用a标签来保证用户能更好的输入验证码。
验证码:<input type="text" name="CheckCode">
<img alt="验证码" id="imagecode" src="<%=request.getContextPath()%>/servlet/ImageServlet"/>
<a href="javascript:ReloadCode();">看不清楚,换一张</a>
效果如图
预览

2.完成生成随机验证码的Servlet

我们使用Servlet来完成对应的功能,创建ImageServlet类并继承自HttpServlet类。编写doGet()方法
package com.yuchi;

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

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ImageServlet extends HttpServlet {
        public void doGet(HttpServletRequest Request,HttpServletResponse Response) throws IOException {
            //BufferedImage将图片存入缓存中,有三个构造方法,此处的三个参数为图片的宽,高,以及创建的图像类型。
            BufferedImage bi=new BufferedImage(68,22,BufferedImage.TYPE_INT_RGB);
            //为bi创建图形上下文
            Graphics g=bi.getGraphics();
            //设置颜色,此处调用的构造方法是基于RGB数值作为参数的
            Color c=new Color(200,150,255);
            //设置颜色          
            g.setColor(c);
            //该方法用于填充指定的矩形,参数是坐标和宽高
            g.fillRect(0,0,68,22);
            
//编写随机获取验证码的部分
            
            //将字符串转换为字符数组
            char[] ch="abcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
            //随机类,在本程序中只使用了 int nextInt(int n) 方法,作用是生成一个0-n的伪随机int值
            Random r=new Random();
            
            int len=ch.length,index;
            
            //用于存储随机生成的四位验证码
            StringBuffer sb=new StringBuffer();
            
            for(int i=0;i<4;i++) {
                //从0-len随机获取一个作为下标
                index=r.nextInt(len);
                //随机获取颜色
                g.setColor(new Color(r.nextInt(200),r.nextInt(150),r.nextInt(255)));
                
                //在图形中绘制指定的String,参数对应要绘制的String以及坐标
                g.drawString(ch[index]+" ",(i*15)+3,18);
                
                //将内容添加到StringBuffer
                sb.append(ch[index]);
            }
            
            //将验证码信息放入session中用于验证
            Request.getSession().setAttribute("PicCode",sb.toString());
            //将文件流输出,参数要写入的RenderedImage,输出的文件格式,输出到的ImageOutputStream
            ImageIO.write(bi,"JPG",Response.getOutputStream());
        }
}

2.5关于doGet()方法中的内容详解(个人理解)

BufferedImage bi=new BufferedImage(68,22,BufferedImage.TYPE_INT_RGB);
查阅了API,BufferedImage类似乎用于将图片存入缓存中,在这里我们调用的构造方法有三个参数,分别对应图片的宽高和创建的图像格式。此处的BufferedImage.TYPE_INT_RGB是一个类成员属性。详见API
TYPE_INT_RGB
//为bi创建图形上下文
Graphics g=bi.getGraphics();
//设置颜色,此处调用的构造方法是基于RGB数值作为参数的
Color c=new Color(200,150,255);
//设置颜色          
g.setColor(c);
//该方法用于填充指定的矩形,参数是坐标和宽高
g.fillRect(0,0,68,22);
API上说“Graphics 类是所有图形上下文的抽象基类,允许应用程序在组件(已经在各种设备上实现)以及闭屏图像上进行绘制。 ”
这里我认为其类似于MFC里那个PDC,获取了屏幕之后,所有的操作都是基于它的,这里的Graphics也是一样,关于颜色,大小等操作都是基于它进行设置的。
setColor()fillRect()方法,前者的参数是一个Color类对象,该类有多重构造方法,而此处使用的是三个int数值,对应的是RGB数值大于0小于256.而后者则用于填充指定的矩形,参数是坐标和宽高。
//编写随机获取验证码的部分
            
//将字符串转换为字符数组
char[] ch="abcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
此处使用了toCharArray()方法将字符串转换为字符数组,如果不适用该方法,char[] ch是没有办法这样赋值的。
//随机类,在本程序中只使用了 int nextInt(int n) 方法,作用是生成一个0-n的伪随机int值
Random r=new Random();
API中将Random称为伪随机,并称Math.random()更容易使用= =!
Random
int len=ch.length,index;
            
//用于存储随机生成的四位验证码
StringBuffer sb=new StringBuffer();
定义了用于存储长度的len以及对应的下标index,并定义了StringBuffer类对象用来存储随机生成的验证码。
for(int i=0;i<4;i++) {
        //从0-len随机获取一个作为下标
        index=r.nextInt(len);
        //随机获取颜色
        g.setColor(new Color(r.nextInt(200),r.nextInt(150),r.nextInt(255)));
                
        //在图形中绘制指定的String,参数对应要绘制的String以及坐标
        g.drawString(ch[index]+" ",(i*15)+3,18);
                
        //将内容添加到StringBuffer
        sb.append(ch[index]);
}
使用for循环生成4次随机字符,r.nextInt(int len)方法用于生成一个随机的0~len之间的int变量。后面的获取颜色中也是如此使用。
void drawString(String str, int x,int y)方法用于将指定文本绘制到图形中,参数分别对应指定的String文本,以及宽高。
最后利用StringBuffer.append()方法将生成的字符添加到类对象sb中。
//将验证码信息放入session中用于验证
Request.getSession().setAttribute("PicCode",sb.toString());
//将文件流输出,参数要写入的RenderedImage,输出的文件格式,输出到的ImageOutputStream
ImageIO.write(bi,"JPG",Response.getOutputStream());
创建session并添加sb.toString()用于实现判断
最后一个方法是我觉得最迷的- -!ImageIO.write()方法,API中有三种构造方法,用于将ImageWriter按指定格式以三种不同方式输出。
ImageIO.write()方法
完成了如上代码的编写之后需要配置servlet打开web.xml添加如下代码。
  <servlet>
        <servlet-name>ImageServlet</servlet-name>
        <servlet-class>com.yuchi.ImageServlet</servlet-class>
  </servlet>
  <servlet-mapping>
        <servlet-name>ImageServlet</servlet-name>
        <url-pattern>/servlet/ImageServlet</url-pattern>
  </servlet-mapping>
部署后运行。
部署后运行
到此处为止,随机生成验证码的功能就完成了。

3.完成刷新更换验证码的功能

事实上我们在页面中使用验证码时往往容易出现验证码看不清的情况,因此我们需要有一个用户按钮使得验证码可以刷新,此处我们给a标签写上JavaScript以完成刷新功能的实现。
<script type="text/javascript">
    function ReloadCode(){
        var time=new Date();
        document.getElementById("imagecode").src="<%=request.getContextPath()%>/servlet/ImageServlet?d="+time;
    }
</script>
此处使用了document.getElementById()获取到img标签之后,直接修改其src属性即可完成刷新,但IE浏览器似乎有一个缓存功能会使得刷新并不能生效,因此需要增加一个时间作为参数,使得每一次刷新的内容都不相同,这样IE才不会认为此次刷新是不需要的。
到这里,整个用户界面就算完成了效果如图
效果预览

4.完成对验证码的验证功能

其实验证功能的原理很简单,取出之前存入Session中的验证码,与用户的填入的验证码对比即可。
新建LoginServlet类,同样继承自HttpServlet
package com.yuchi;

import java.io.IOException;
import java.io.PrintWriter;

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

public class LoginServlet extends HttpServlet {
        public void doPost(HttpServletRequest Request,HttpServletResponse Response) throws IOException {
                String PicCode=(String) Request.getSession().getAttribute("PicCode");
                String CheckCode=Request.getParameter("CheckCode");
                
                CheckCode=CheckCode.toLowerCase();
                
                Response.setContentType("text/html;charset=gbk");
                PrintWriter out=Response.getWriter();
                
                if(CheckCode.equals(PicCode)) {
                    out.print("正确!");
                }else {
                    out.print("错误!");
                }
                out.flush();
                out.close();
        }
}

这一次我们选择使用post传参,因此方法也变成了doPost()【实在因为我对get那种URL传参的方式有点厌恶……】。
在类的编写中需要注意到几个问题
  • 验证码的大小写问题
  • 字符编码
  • 输出流的关闭
因此在类中我们编写了一些用于解决这些问题的语句
//将用户的验证码统一为小写
CheckCode=CheckCode.toLowerCase();
//设置页面的字符编码为gbk
Response.setContentType("text/html;charset=gbk");
//刷新流并关闭
out.flush();
out.close();
编写对应的Servlet并更改HTML结构
  <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.yuchi.LoginServlet</servlet-class>
  </servlet>
  <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/servlet/LoginServlet</url-pattern>
  </servlet-mapping>
<form action="<%=request.getContextPath()%>/servlet/LoginServlet" method="post">
        ......
        <input type="submit" value="提交">
</form>
以上,一个基本的验证码功能就完成了。效果如图
基本演示
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,015评论 11 349
  • 我不知道一个人该在什么状态下想起另一个人,我不知道想起另一个人意味着什么,我也不知道和想起的这个人的生活会永远平行...
    结硬寨打呆仗阅读 238评论 1 1
  • 01 如果说湘云是侠女,遍历一下金庸小说里的女侠们,要找到一个做类比,我大概会选霍青桐。同样是心思光风霁月,有不输...
    彼岸沙阅读 849评论 1 2
  • 有的小朋友觉得二胎会占用父母对他们的爱,可是我并不这么认为。我倒觉得有了我们家的妹妹给我们家增添了许多份温暖...
    要减肥的二宝阅读 149评论 0 1