NETCTOSS代码实现第七版

前言

登录模块之验证码功能

验证码功能需求分析与设计

  • 那现在啊,咱们就是了解了cookie和session啊 ,反正也知道它们怎么用了,那下面我们再做一个案例,那这个案例呢,可能是用cookie,或者是用session来做,那到底用哪一个,我们再,具体再分析啊,再想,那你看啊,做哪个案例呢,就是做我们电信计费项目当中的一个案例,就一个场景啊,看一下,咱们这个项目的登录功能上,还有个东西没处理,验证码对吧,之前没处理了,是因为没有学cookie和session,处理不了,现在可以处理了,然后你想啊,我们想一下,这个验证码这个功能是怎么用的,首先呢,当我打开登录页面的时候,是不是就应该出现一张图片,然后呢,图片里写什么,我就照着写对吧,比如说8251,和它一致对吧,一登录,登录的那一刻,服务器得判断,你这个,你输入的验证码和图片的对不对,对吧,如果对了可以去,如果不对的话,还得重新输入对吧,是这样。
    在这里插入图片描述
  • 所以呢,关于验证码呢,在使用上,有两个这个细节,或者说有两个环节,第一个打开网页时要出现验证码这个图片,第二个输入以后,登录时要验证对吧,两个环节。那咱们一个环节一个环节来探讨啊,我们先不管验证,我们只管这个图片,那你看,现在这个图片是不符合要求的,它是死的对吧,它是固定的,那我们这个图片应该是动态的,应该是每次访问,或每点一下,它得变对吧,得有所变化,那大家再想一想,我想让这个图片变,那你说这个得怎么办,有没有什么思路去解决这个问题,有人说定义单击事件,那是后话,首先我们打开这个网页,这个图片就得生成对吧,就得有一个图片,就得是动态的,就得是,每次刷新是不是得变啊,每次访问都得不固定,得变啊,你看这个图片得怎么来呢,从何而来呢,就是网页上需要一个动态的图片,这个动态的图片,我们怎么办。
  • 有人说画啊,画,用什么画,对象,用对象来画,什么对象啊,能具体点,是吧,哎,或者这么说吧,咱们先不谈讨这个图片呢,怎么画出来的,就是说你想啊,是,我要画图片,不管怎么样,我得动态生成一个图片,对吧,别管是怎么画,还是怎么招,我得动态生成一个图片,得动态产生一个图片,这个动态产生图片的这段逻辑,这段代码,我在哪里写,我在哪个地方写,我是在Servlet里写,是在jsp里写,还是在,还是写js,还是写什么,用什么写,在哪里写,你看这个界面啊,现在很多同学,一看这个界面,有点发蒙,这代码怎么写的,都忘了,是吧,你看啊,我们看一下之前的那个图啊,别乱。
    在这里插入图片描述
  • 你看,这是我们之前呢,所讲的登录功能的流程对吧,哎,我们敲一路径,打开了登录页面,这登录页面呢,也是动态的啊,然后呢,点登录啊,提交登录的请求/login.do啊,等等,你看在众多环节当中,是在MainServlet.toLogin()里写,是在/WEB-INF/main/login.jsp里写,还是在哪个地方写,你看啊,在jsp里写,jsp里怎么写呢,注意啊,这个大家可能还是对这个啊,浏览器访问服务器获得网页,以及对网页的加载过程,还是没有理解透,即便是以前讲过,那么你还是没有理解透啊,之前我们讲了,我说了什么呢,浏览器访问服务器,得到一个网页,得到的是什么,仅仅是html,我们在这个请求当中,有可能同时得到一个html,又得到一个图片,有可能么,不可能,浏览器得到网页,加载网页时才会去获取图片,是这样吧,就是说浏览器获取网页,获取图片,获取css,是分步的是吧,是分开的,是这意思么。
  • 所以浏览器得到图片,是一个独立的行为,是和得到这个网页无关的行为,是这样吧,所以,你说在jsp这里写,你觉得合适么。/toLogin.do/WEB-INF/main/login.jsp,这个请求是为了生成html的是吧,那你还得有一个请求用来生成一个图片,是这样吧,是分开的。好像有人,还没明白,我能说什么呢,看一下吧,之前讲的那个话题啊,就是我们在讲完第一个功能,查询以后,就说过这个话题。
    在这里插入图片描述
  • 我说了啊,就是说浏览器访问服务器获得网页,以及加载网页的过程当中包含多次请求,我们在讲这个话题的时候,还看了Network对吧,看到了,确实是多个请求啊,那么当我们呢,调用findCost.do的时候,我们得到的是什么呢,仅仅是一个html,那这个过程中,是不可能同时再返回一个图片的,明白么,你只能,就说白了,服务器向浏览器输出东西的时候,它必须声明输出的格式,如果你声明的格式是html,就没有图片,明白么,如果你输出的格式是图片,就不是html,明白吧,它只能输出一种类型,不能两种一起输出,明白吧,不可以。所以呢,这个findcost.do--MainServlet.findCost()请求当中呢,只能输出html,那html加载的时候呢,访问服务器,再得到一个网页,而这个时候可以,单独输出一个图片,就是说,我们想这个向浏览器输出一个图片,怎么说呢,应该是一个独立的行为,独立的请求,单独写一个。
  • 当然了,我这么说的话,意思是说,这个图片应是服务器生成的,那可能也有人会想,能不能说我向浏览器输出了网页,在加载网页时,我在网页上onload里,我用js拼个图片,能不能行,你看,我得到网页了是吧,我不访问服务器,我不跟你服务器要图片,我在这个网页加载时,我html里写js,我用js,页面加载后,我拼一个图片,那样行不行,为啥不行呢,有人说到点子上了,这是安全性的问题,安全性,你想啊,这个图片,如果是在客户端生成的,客户端这个html也好,js也好,css也好,咱们是不是能看到啊,右键查看源代码或者是F12,看那个Source,这些(前端)代码都能看到对吧,那你的js的逻辑,暴露在别人的面前,你是怎么拼的,这个逻辑一目了然,他很容易破解的,明白吧,听明白了吧,这是很不安全的,是这样吧,不安全的。
  • 那么,我们在服务器端,写这个代码,服务器端的代码,java代码的逻辑,客户端看不到明白么,不知道,它不知道你咋拼的,他只能猜啊,那他破解难度会大大的增加,会更安全,所以呢,这个拼图片,一定是服务器去拼的,我说拼的是这个验证码图片,服务器拼,为了这个更安全啊。那这个具体的开发流程,我再详细的再说一下啊,这是对着以前的案例啊,我们回顾一下,比如说,其实有一些关键点,以前我在讲其他的案例时,其实也涉及到了,可能是大家忘了,回顾一下,然后,我们再说这个案例,那有些事就好说了啊。
    在这里插入图片描述
  • 左侧这是浏览器啊,然后呢,右侧这是服务器,现在呢,浏览器要访问服务器,这是要打开登录的网页啊,那么打开登录的网页呢,我们在浏览器上是敲了一个地址,是/toLogin.do对吧,这个请求我们已经写了,但是呢,这个图片的生成和这个请求是有关联的,所以,我把它也画出来啊,当这个用户呢,敲了这个路径以后,访问了服务器,那服务器呢,我们是用Servlet处理了这个请求,那就是MainServlet当中的toLogin方法,MainServlet.toLogin(),这个方法呢,它没干什么,它就是转发了对吧,它把请求转发给了这个login.jsp,然后呢,由login.jsp向浏览器呢,做出响应,那么在响应之后,浏览器就得到了,它输出的html,是这样吧,我们这个jsp上啊,那个指令里没有写什么呢,没有写contentType,那如果没有写的话,我说了,默认值为html对吧,所以本次响应输出的仅仅是html,没有图片啊,只是html。那么这是第一个请求啊,打开登录页面。
  • 那浏览器得到这个网页以后呢,它要对网页加载,那加载的话呢,它是分步啊,先加载head,再加载body,那我就一步到位了,这个head,我不写了,就只写body,总之呢,浏览器啊,需要对网页进行加载,就是它得到的是一个文本,它需要把这个文本转换为对象对吧,并显示啊,这个过程我们叫加载,那在加载body的时候啊,那它发现了,body里有一个标签,那个标签呢,叫image,而且标签上是有路径对吧,它知道啊,是要找这个图片,于是呢,这个浏览器就向服务器发出一个请求,要求服务器返回路径相对的那个图片,那这个网页上啊,之前我们写的那个路径是什么呢,我要没记错的话是这个valicode.jpg啊,好像是这样,你可以看一下login那个页面,应该是这个。
  • 然后呢,浏览器单独访问它valicode.jpg,才得到了那个图片,所以img,图片这个请求是第二个请求,是一个独立的请求,这个我们之前是没有管对吧,自动的,那现在呢,就有问题了,现在的问题是什么呢,就是这个请求,我们给它返回了一个静态的图片,这个不合适是吧,现在不能是静态的,是要动态的,所以我们需要呢,做的事情是把原先这个valicode.jpg,这个静态的资源换成动态的,那你想啊,我把它换成动态的,我得写个啥,我得写什么呢,那你看,我们这个javaEE讲的就是什么呢,我们怎么向浏览器输出资源对吧,那么,输出动态资源两种方式,要么Servlet,要么jsp对吧,或者是结合起来,servlet+jsp对吧,是吧,也可以,MVC模式,都行,那你看这里,我用谁呢,其实都行,但是呢,jsp里呢,更适合写标签,是吧,更适合写html,那我们现在输出的不是html,我输出的图片对吧,是一份二进制的数据,所以用什么来处理呢,Servlet,我们是要输出一个,显然是一个流对吧,字节流,图片么,对吧,肯定是字节流。
  • 所以呢,换成动态资源的图片这里呢,我们得写,得用Servlet来代替这个jpg啊,那我们项目中呢,只有一个Servlet,就叫MainServlet,那我需要呢,给这个类中呢,加一个方法,单独处理一个请求,那么这个方法我叫什么呢,我叫createImg,行吧MainServlet.createImg(),叫创建图片可以吧,就叫这个名字吧,然后呢,由这个MainServlet.createImg(),动态资源来代valicode.jpg,这个静态资源,来代替它,那这个东西呢,这个createImg()方法才是一个关键,在这里呢,我们需要呢,就是动态的拼一张图片,向浏览器呢,输出这张图片就可以了,那怎么动态拼一张图片呢,咱们其实以前也干过,学那个飞机大战的时候,不也画过么是吧,其实也画过,但是呢,我们画的时候,用的是那个javaswing当中的一些东西啊,那这个东西啊,其实java也可以做这个网页,java也可以做客户端的。
  • 不过呢,java做客户端的话,用swing做的话,非常的繁琐,非常的麻烦,那么java呢,在这方面呢,没发展好,很少有企业用这种东西,不常用,一般不用啊,所以这个是次要的东西,我们这个4个月的课也没有以这个为主,所以不管是讲飞机大战也好,还是现在呢,我们要画一个,验证码图片也好,这个都是,我们把它作为一个次要的地位来对待,明白吧,因为我们主要的内容是网页,是javaEE啊。所以呢,生成图片的过程呢,时间原因,我把它提前封装好,咱们直接去调用,如果你感兴趣,自己看一下,那如果工作时呢,你要自己在工作中拼一个图片的话,你可以参考这个代码,明白吧,比如说我这拼的是,这个英文加数字,你工作时,想拼汉字,你可以改对吧,可以改啊,反正就是你可以参考。
  • 那么,这个封装好的类啊,叫做ImageUtil,这个类啊,之前呢,都传给大家了啊,然后我们看一下,所以Servlet就调这个工具类,就行了,就调它就可以了,调它,它会给这个Servlet返回这个图片,然后呢,它把图片用流的方式输出给这个浏览器就行。当然了,这里面还有一些细节,这些细节我们开发时,遇到时再解决啊,这个别着急,总之呢,你看啊,整个过程是,我们敲这路径/toLogin.do,访问登录页面,服务器呢,会给我返回这个登录网页,在网页加载时,当加载到这个标签时,访问服务器,得到一个图片,之前图片是静态的,我们现在把它变成动态的,就要单独写个方法,明白吧,那之所以呢,没有把这段逻辑,写到这个Servlet和jsp里,是因为这件事跟他们是没什么关系,对吧,是分开的,是独立的,你再写到jsp这里面来了,就很奇怪了。
  • 那然后呢,这个页面加载完成以后啊,那很显然,我们会在服务器上,就看到这个表单了,对吧,就能看到这个表单了,那表单里就会有这个,就会有验证码,那么验证码呢,是一个框,框的后面呢,还有这个图,你是参考图来输入这个数据,后面还有这个图,就这样,图里有字,当然我这画的没有字啊,了解一下。然后呢,用户呢,就可以输入这个内容,然后呢,点登录按钮,访问服务器啊,把这个数据提交给服务器,是这样,那么,当访问服务器的时候,把这个数据提交给服务器,那这个请求,也写过,就是那登录验证对吧,登录检查么,我们访问的是谁呢,是MainServlet里面的login方法,MainServlet.login(),那之前呢,这个方法当中呢,我们只是接收了账号密码,对账号密码做了判断,那现在再加一段逻辑,对验证码也做判断就可以了,做一段逻辑就行。那么你要判断,这个验证码是否正确的话,你首先得得到这个验证码,得到用户输入的对吧,那我们需要给这个框呢,取个名字,那比如说这个名字呢,那我叫什么呢,叫,叫code行吧,可以吧,咱们账号叫adminCode,验证码叫code,并不矛盾,对吧,不一样,就叫code。
  • 然后呢,在MainServlet.login()这里面我得到了code,我code要和图片中的那个字去比,那图片中的字怎么得到的,那又出一个新的问题了,图片里的字怎么弄。想一想啊,思考一下这个字怎么得到,收藏,怎么收藏,就是说我在提交表单时,能不能把图片中的字也一起提交过去,可不可以,行不行,可以,怎么提交,给这个图片加个name,就提交过去了,你想的也太美了吧,咱们以前讲表单时啊,说表单有两要素,form,声明范围,目标对吧,控件,控件有12个,9个input,3个其他的对吧,能输入数据,数据呢,可以提交给服务器,我们讲过说,除了这12个之外的,其他的像段落,标题,能提交么,图片,超链接,能提交么,都不能,其他都是只读的,对吧,都没有办法提交,没有这个功能,它不具备这个能力啊。
  • 那你看,那我现在这个图片中的字得不到,想想办法,怎么办,想想办法,就这个解决方案,才是我们当前这个关键点,关键之处啊,那怎么办,就说出你的想法啊,错了也没关系啊,错了怕什么啊,完了,越说越担心是吧,怎么弄啊,有人想到了,就是说,我们这得不到,就是第3个请求当中,没法得到对吧,但是你看,第二个请求,我们产生了验证码这个时候,我能把它记录下来,在第3个请求这再用,是不是可以啊,可以记录在这用,对吧,那你这注意了,这个第二个请求记录这数据(验证码),给第3个请求用,跨请求了,对不对,你得用什么来记,显然是cookie和session能解决问题,对吧,就别考虑什么request了,config和context对吧,别考虑了,就cookie和session,这才切入我们的正题,我们这段讲的就是cookie和session么。
  • 那具体说,我们用cookie还是session呢,用哪一个呢,session,为啥用session,因为session存数据,存到了服务器里,为了安全,验证码是不是要保证安全呢,需不需要呢,肯定是需要的,对吧,如果你存到浏览器上,那人家一看就看到了对吧,懂技术的能看到,那就遭了,很容易就被破解了,所以呢,一定是存在session里啊,因此啊,我们在这里,需要呢,将这个imageUtil返回的那个验证码,存入session,就是imageUtil啊,给这个它,给MainServlet.createImg(),返回的是两个值啊,一个是图片,一个是这个验证码,验证码是个字符串,就是它返回的是两个值。
  • 那我们得到这两个值以后啊,我们就需要呢,那个验证码存入哪呢,我们需要把验证码存入session,你在这MainServlet.createImg(),把验证码存入了session,那我们在这,第3个请求MainServlet.login(),可以得到这个验证码对吧,就行了,但是这个验证码这个名字,就取个名字吧,就叫imgcode,这样行吧,就图片的code,我们就把它存入这个session,那么在第3个请求当中呢,我们这,MainServlet.login(),能够接收到code,我们从session中取到imgcode,两者相比就可以,是这样吧,那验证码比较的时候,通常是区分不区分大小写,不区分大小写。
  • 那我们怎么去比较,才能不区分大小写呢,这俩字符串,有人说,我把它都转成大写,或都转成小写对吧,一比较就可以了,可以这样,但其实呢,也没必要,这个String当中有一个方法,可能之前大家没有学过啊,那个方法叫什么呢,叫equals,还没完,IgnoreCase,学过没有啊,没有,没有没关系,这个不学了么,code.equalsIgnoreCase(imgcode),这个方法比较长,叫equalsIgnoreCase,这个equals是等于对吧,ignore是忽略,case是大小写,就是连起来是啥意思,就是比较两个值是否想等,但是忽略大小写,明白吧,它底层自动转换成大写或小写了,所以这个就省点事了啊,用这个方法。
    在这里插入图片描述

验证码功能代码分析

  • 那后续啊,这个比较那就容易了,你只要知道这个方法,怎么比较很容易,这个案例的逻辑啊,关键点,其实主要呢,是在于这个地方,在于第2个请求,那我们再把这个逻辑呢,再看一下,别乱啊,首先说了,浏览器访问服务器得到登录页面,以及它加载登录页面,加载上面的图片,分开的对吧,不同请求,那么在它加载图片的时候,原来,我们给的是一个静态路径,现在,再把它换为动态的对吧,我们需要单独写一个Servlet处理,因为本次请求输出的是图片,是二进制的数据啊,用Servlet处理比较合适,jsp写标签不太合适,然后呢,生成图片的过程,我做了封装,封装成一个工具类,你都可以去调,可以去使,那它给我们返回的是图片和验证码,其中呢,验证码要存入session,好给第3个请求用对吧,在这个时候呢,将用户输入的验证码和图片中正式的验证码相比,比较时呢,忽略大小写,这是注意的地方。
  • 那么要想开发这个功能啊,我们得一个一个来,那现在呢,整个流程当中,第一个请求是已经完成了对吧,不需要做了,而第2个请求正是需要做的对吧,这个valicode.jpg图片不需要了,我们需要写这个MainServlet.createImg(),需要写ImageUtil,那因为组件,就Servlet依赖于Util对吧,就我们先写Util,这ImageUtil呢,我之前上传了,找到它,能看一下,试一下啊,打开你的这个servlet-doc,之前下载的那个目录啊,servlet-doc。那servlet-doc之下有一个java文件,类啊,叫ImageUtil.java,能找到么,找到它以后啊,你把它复制一下,然后呢,把它粘贴到我们的项目中来,那你看这个类,我们放到哪个包下比较合适啊,显然是util对吧,从名字就能看出来,看一下啊,放util包下。
    在这里插入图片描述
  • 放好以后啊,我们就打开这个类,咱们不写,但是我们大概过一遍,看看里面大概有什么啊,你工作时啊,如果想做这样的逻辑的话,你可以参考这个ImageUtil.java,照着改,那我这个做的比较简单,稍微简陋一点啊。首先呢,声明一堆常量,这个第一个常量呢,是这个char,那咱们生成验证码,那里面要写字对吧,这个字得有一个范围,多少范围呢,我声明的是,比较少,是0-9数字,还有这个A-Z,26个大写的字母,对吧,你还得加上小写的,那其实很多网站呢,会把这个什么去掉呢,会把这个L,会把这个I,因为这个,L和I,尤其是小写的,它和这1,不好区分明白吧,还有这个什么哦O,O和0对吧,也很难区分的,这个不好看,可以把它去掉,那我这个都没去掉,因为这就这样了,你可以自己再修饰一下啊,可以加一些小写的字母,甚至呢,可以加这个,一些汉字什么都可以啊。
  • 然后呢,还有一些常量啊,就是验证码,我们拼的时候是拼几个字符,我这是4个,一般都是4个对吧,你也可以6个,可以几个啊,一般都是4个,可以多一点,然后你还要注意,咱们这个验证码图片啊,不是为了好看,是为了安全对吧,是为了防止用户,就懂编程的人的暴力破解,因为如果你没有验证码的话,没有一个人为的验证的话,他有可能是编一段程序,不断的瞎猜你的这个账号密码对吧,它不断地这个输入,随便拼任意的账号密码,万一给你破解了呢,那么,有了这个验证码以后的话,这个电脑啊,写程序,它再怎么聪明的话,它不好识别图片中的字明白吧,它不好识别。
  • 所以为了增加呢,程序中识别文字的难度,我们需要对这个字,对这个图片还加以干扰,我们需要画几道干扰线明白吧,画几道线,当然这个验证码呢,这个对暴力破解的程序干扰,不同的网站,不同的做法,有的网站是干什么呢,它不是画干扰线,是画点,一大堆点,有的网站是画什么呢,把这个文字啊,把这个做出3D的,对吧,3D化,计算机不好识别,有的是把它扭曲了,有的是拉伸了对吧,反正还有的是,有的是出一道题,1+1等于几,你写那个2,那样的,更有甚者像那个12306对吧,问你下面哪个是樱桃,就这个,考你抽象能力的,电脑再聪明,它没有抽象能力对吧,它无法识别,所以说,这个更高级了啊。
  • 总之啊,验证码是为安全,不是为了好看啊,我们这里呢,处理的方式是画几道干扰线,明白吧,画完线以后啊,那个图片看起来不好看了,但是呢,人呢,仔细一看,还能识别,人有抽象能力,计算机识别不了,这个线呢,画几条都行,你画的越多越不好识别,你画太多的话,自己都看不出来了,一般我们画个5条左右,差不多了,画太少也不行,你画一条线,计算机可能会识别,明白吧,还不能太少,5条 ,然后呢,图片多大啊,宽度是80,单位是像素默认啊,高是40,这个可以调,觉得小,可以调大,也可以调小,然后呢,图片上的字,字号多大,30像素啊,这都有设置啊,总之呢,这些是参数,是可调的,如果你想做的更好的话,也可以把它做到配置文件里,明白吧,读配置文件去改也行,这里就简单处理了啊。
  • 然后呢,是提供了一个方法叫createImage,那这个方法呢,能够创建验证码图片,返回的数据是一个数组,因为之前我说了,我们这个工具啊,要给调用者返回的是图片和图片中的字对吧,是吧,因为调用者得到图片以后,它也不知道,图片里是啥字,你得告诉他,两个值,一个是图片,一个是字,所以呢,返回多个值,我是用数组来封装的,当然了,你不用数组的话,你用这个集合啊,Map啊,对吧,你写一个实体类啊,这都可以啊,我是用数组,然后呢,这个里面的逻辑啊,其实,咱们以前学飞机大战时呢,也是应该有所了解。
  • 首先呢,我要画图片,用java画,我要创建一个空白图片BufferedImage,然后呢,创建时指定它的宽,高,和这个配置的方案,BufferedImage image = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB);,然后呢,我要在图片上画东西,我要获取画笔,啊,Graphics graphic = image.getGraphics();,取到画笔graphic,然后呢,给画笔设置个颜色,这个是灰色LIGHT_GREY,浅灰色,然后呢,我们再画这个验证码的时候,先给它画一个背景啊,所以,先画一个,就在这图片上啊,画一个方块,矩形的方块,graphic.fillRect(0, 0, WIDTH, HEIGHT);,这个方块的背景色就是笔的颜色,灰色啊,那么画方块是从左上角画到右上角,你只要声明,左上角的坐标和右下角的坐标就可以了,左上角坐标为00,右下角坐标为宽高,这能理解吧,如果左上角为00的话,右下角不就是宽高么,对吧,这样。
  • 然后呢,我们要在图片上,再画5个随机字符啊,这是循环5次,然后呢,每次画啊,那随机的话,没有用那个math.Random,用的是Random这个类,这个类啊是,java.util之下的类,它呢,也能生成随机数,它生成的随机数呢,是指定范围之内的整数,明白吧,就是指定范围之内的整数,就不用取整啊,就这个比那个math.Random还要,有的时候,还要简单一点啊,Random ran = new Random();,用这个,然后呢,每次循环我们要画一个字,写上一个字,那我就随机一下啊,范围是从0到chars.length,取那(验证码字符集)数组的长度,得到一个随机的下标,下标对应的是那个字,int n = ran.nextInt(chars.length);
  • 然后呢,再给画笔设置个随机颜色,graphic.setColor(getRandomColor());,每个字颜色都不一样,也是为了增加破解的难度啊,然后呢,设置这个字号啊,然后呢,就画这个字啊,把它画在不同的位置啊,graphic.setFont(new Font(null, Font.BOLD + Font.ITALIC, FONT_SIZE));,比如第一次,我们把它画在这个,矩形的最左端,第2次,往右偏移点,第3次再往右偏移点,明白吧,这里有个简单的运算啊,乘以一个系数啊,可以自己详细看一看。
  • 然后呢,画的这个字就是chars[n]啊,下标,graphic.drawString(chars[n] + "", i * WIDTH / SIZE, HEIGHT / 2);,字符画好以后呢,把这个字呢,记录下来,记到StringBuffer里,那么循环4回以后啊,这个验证码就画好了,图片就画好了,就是字就有了,有了以后呢,还得画这个干扰线,所以后面呢,再循环一遍,for (int i = 0; i <= LINES; i++) ,循环这个是5次,然后呢,每次也是啊,设置随机色,画一条线,画一条直线,你要指定这个线的起点和终点,是两个坐标啊,俩坐标是随机生成的,在矩形范围内,随机生成俩坐标,画条随机的线,不知道从哪画到哪,不一定,然后呢,最后返回的是验证码和图片,是一个数组啊。这是大概的流程啊。
  • 然后呢,咱们在写这个方法的时候啊,有的地方设置这个随机色,这个随机色有单独的方法,封装了一下,那随机色的话,就是我们随机产生一个颜色,而一个颜色由3个值构成,红绿蓝对吧,然后呢,就随机生成这么一个范围内的值就可以了,这就了解下,然后,最后还有个main方法,对这个代码进行测试,咱们也可以测一下,看看行不行,这测试代码都写好了啊。

public static void main(String[] args) throws IOException {
	Object[] objs = createImage();
	BufferedImage image = (BufferedImage) objs[1];
	// /home/soft01/1.png
	OutputStream os = new FileOutputStream("d:/1.png");
	ImageIO.write(image, "png", os);
	System.out.println(objs[0]);
	os.close();
}

  • 首先,我们调createImage(),方法得到一个数组,数组中呢,第2个值是那个图片,我们想输出这个图片看一下,目前呢,没有浏览器啊,没有浏览器访问它,我们就输出到硬盘上,那我们就创建一个输出流,然后呢,这个输出的目标是d盘,名为1.png,这个java语言呢,输出图片的话,输出png,这个是更好的,它处理png的图片是更,怎么说呢,更这个细腻啊,更准确,效果更好,所以一般就是输出png,当然了,Linux呢,没有d盘,你得写这样的路径对吧,/home/soft01/1.png,这样的,你得把它改成这样,改一下这个。

  • 那么改完以后,然后呢,我们想输出图片,可以用这个工具啊,叫ImageIO, 然后呢write,write时呢,第一个参是输出的图片,第二个参呢,是图片的格式,第3个参呢,输出流,那么它会调用输出流,帮我们输出,明白吧,调这个工具就可以了,然后呢,最后把流关掉就行了,我试一下,我执行这个main方法,执行完以后呢,看一下d盘有没有图片,看一看,或者看的是soft01啊,我打开d盘,确实d盘之下,有了一个图片叫1.png,你看它的时间,它的时间是2017年5月24号11点07对吧,最新的,刚生成的,热乎的啊,然后你看,我把它打开看一下,对吧,就这样的。
    在这里插入图片描述

  • 有人说这太难看了,对,就是要难看啊,难看的目的是为了避免被破解对吧,对,不是为了好看,是为了避免被破解啊,这就行了,挺好了。那目前呢,已经把这个ImageUtil,把这类呢,拿过来,经过测试啊,可以了,那么有了这个类以后啊,下面呢,我们就需要写这个 Servlet,MainServlet.createImage(),然后在这个方法当中呢,下面呢,我们需要调用这个ImageUtil工具,然后呢,生成图片,那对于返回的结果,两个结果啊,一个是图片,一个是验证码,那对于图片呢,我们要把他输出给浏览器,而对于验证码,要存入session,那么,怎么把图片输出给浏览器呢,咱们刚才呢,在这个类当中,不看到那个main方法么,对吧,那个main方法是怎么去,把图片输出到d盘,我们就可以怎么把图片输出给浏览器,只不过目标不同,一个 是d盘,一个是浏览器而已,那有人说,那浏览器 谁,我不知道啊,你不知道没关系,服务器知道。

  • 那么我们通过服务器呢,能够直接得到,我们要用的那个输出流,可以直接用。就是说,我们想把图片输出给d盘,我们得自己new一个,这个输出流,如果呢,是想把图片输出给浏览器,这个输出流,我们可以直接获得,你像以前,我们像浏览器啊,拼网页,咱们不是直接获得一个Writer么,是吧,字符流,其实我们还能通过response得到一个字节流啊,可以输出这个东西。那下面我们写一下这个啊,这个少点,忘点事啊,就是说,MainServlet.createImg()的访问路径是什么,给标一下啊,路径呢,和createImg(),这个方法是对应的啊,它的路径叫,/createImg.do,和这个方法对应,这是访问路径啊。

  • 那下面呢,我们就来处理这个请求啊 ,那我们打开MainServlet, 打开以后啊,我们在这个类里啊 ,找到那个service方法啊,然后呢,在service方法当中,对这个请求进行判断,那么在service里呢 ,我再加一个判断啊,else if("/createImg.do".equals(path)){ createImg(req,res); },那么,如果呢,是这个路径,我们就调用对应的这个方法,那下面呢,我把这个方法呢,声明一下,那方法声明与其他方法一样,复制粘贴就行,那么我就直接啊,在service方法之后啊,再粘贴一个方法,然后呢,把它改名加createImg,然后呢,写注释啊,加以解释啊,这个方法的作用啊,是生成验证码。

  • 那么这个方法之内,我们主要做的事情就是生成验证码,那我们可以调用那个工具啊,ImageUtil啊,我把这个代码呢,注释呢,写一下啊,就是生成验证码,以及图片啊。Object[] objs = ImageUtil.createImage(); 那么,我调的这个方法得到的是数组啊,数组中的第一个值呢,是验证码,第2个值呢,是图片,是这样么,我看一眼啊,对,第2个值是图片啊,第一个值呢,是那个验证码,那么对于这两个值呢,我们处理方式,不一样的啊,那么对于验证码,我是要存到session里去,就是将验证码存入session:

    HttpSession session = req.getSession();
    session.setAttribute(“imgcode”, objs[0]);

  • 我就获取了session,然后往session中存值,那个名字呢,叫做imgcode,后面取的时候要用啊,这要记住,那值呢,就是数组中的第一个值,就是验证码,存了,然后呢,还有第二件事啊,我们需要呢,把得到的这个图片啊,将图片发送给浏览器,那我们在那个工具类的main方法里啊,我们是有一段测试代码,是把图片输出到d盘啊,那我们把图片发送给浏览器,差不多,但是呢,也有点这个区别,那我们想向浏览器呢,发送东西,首先得声明对吧,我得告诉你,我给你传的是什么东西,对吧,得写一句话,写什么呢,你得写res.setContentType("");,是这样么,是吧,你得写这句话,你看啊,以前呢,我们写这句话,这里面写的是text/html对吧,那现在呢,你还能写text/html么,能么,不能了,因为你现在输出的不是网页对吧,是图片了,那图片你说我怎么写呢,注意啊,这个不要乱猜,这个地方,这个字符串怎么写,是有明确规定的,哪里看规定呢,之前给你演示过,你去那个文档,rlc2616好像,那个是http协议的规定对吧,那里面有规定,得看那个文档,文章啊。

如何查询响应消息头contentType类型
  • 不过那个文章呢,不好看啊,都是英文的,还很长啊,不好找,那我们还有一个办法。因为呢,tomcat,它遵守了这个协议,它实现了这个协议,那么tomcat里面有这个声明啊,tomcat里面有这个声明,我们可以看tomcat啊,这样方便,那tomcat这份声明在哪里写的呢,在配置文件里啊,但是你想啊,我们想看tomcat配置文件,怎么看,你得看那个Servers对吧,这个项目,所以呢,我们展开Servers这个项目,那么,这个项目之下啊,有若干配置文件,咱们之前用过哪一个呢,server.xml,这里面可以改那个端口,对吧,可以声明啊,get请求路径的编码对吧,现在呢,不是看这个啊,我们看的是什么呢,这个web.xml,你看tomcat之内,也有一个web.xml,看这个。
  • 那我们打开这个web.xml,看一下,当然了,它这个文件代码可就长了,非常的长啊,然后呢,我们往后看一看,那某些内容,咱们还是能看得懂的,会有这个,怎么说呢,会后到一些启发的啊,那前面呢,都是注释啊,我需要看哪个地方呢,好像是500多行吧,我记得好像是500行以后了,有个地方,有段代码是我们所认识的,找到了,大家找到580行,看到了么session-config,刚写过对吧,就为什么tomcat,它管理这个session,自动30分钟销毁对吧,因为这声明了,哎,那有同学想你这也能改,咱们在我们项目中的web.xml也能改这个东西对吧,那你说这两地方有没有什么联系呢,我改这和改那,有什么区别么,我改这也可以,但是,改这的话,所有项目受影响是吧,我们改我们项目之内,只有这一个项目受影响是吧,就这么个区别,但是呢,你看,我们在我们项目中,也是写这段代码,我们那个项目中也叫web.xml。

<session-config>
	<session-timeout>30</session-timeout>
</session-config>

  • 这俩有什么关系呢,其实,tomcat在启动时,它就会读它的这个配置文件,那我们项目中的web.xml是对tomcat这个配置文件的一个补充明白么,其实是一个补充,那如果说,我们项目中web.xml中,没有写这段话,那么它以它这段话为准,如果我们写了,以我们那个为准明白吧,我们在项目之内的web的配置文件里啊,可以把这里的这个东西覆盖掉,或者你也可这样认为啊,我们项目当中的web.xml,它继承于这个Servers当中的web.xml这个配置文件,你可以这么理解,明白吧,可以这么理解,就总之啊,我们项目当中的配置文件,是对这个文件的补充,它们其实就是一个文件,就是一种文件,格式都是一样的。
  • 然后呢,再看啊,从591行开始,有很多这样标签,叫<mime-mapping>啊,是一种映射关系,是一种对应关系,每一个mapping是声明了一种对应关系,当然,这里面有很多呢,内容,我们是看不懂的,有很多这个对应关系看不懂,我们也不用都看太多了啊,我们找一个能看得懂的,那我们搜一下啊,ctrl+F搜一下,搜什么呢,搜html啊,搜索html,搜一下啊,这个文件中呢,有好几个地方,有html啊,我是想,我想找哪一个呢,看一下啊,860行,不是我想要的,再往后搜啊,1037行也不是我想要的,再往后啊,哎,我找着了,1876行啊,你看,它声明的是什么呢

<mime-mapping>
	<extension>htm</extension>
	<mime-type>text/html</mime-type>
</mime-mapping>
<mime-mapping>
	<extension>html</extension>
	<mime-type>text/html</mime-type>
</mime-mapping>

  • 它声明了html这种格式的文件,我们在输出时,应该写这句话,<mime-type>text/html</mime-type>,就res.set时应改写这句话text/html,听明白了么,就为什么,我们要想输出html时要写这句话text/html,因为这的声明,它规定了以html为后缀的文件,格式就得这么写明白吧,听明白了吗,你差一个字母都不行,你差一个字母,浏览器无法识别,无法识别会怎么样呢,它认为这是一个特殊的文件,会让你保存明白吧,不会显示正确的内容。还有啊,咱们那个html啊,是不是可以省略l啊,可以叫htm对吧,那以这种后缀的文件,也是这格式,听明白了吗,要这样,那如果你想输出其他格式的内容怎么办呢,搜呗,对吧,搜那个后缀,我现在想输出什么呢,png对吧,哎,我们搜一下png啊,png搜一下,一搜呢,发现,在3075行对吧,

<mime-mapping>
	<extension>png</extension>
	<mime-type>image/png</mime-type>
</mime-mapping>

  • 它规定了以png为后缀的文件,格式必须image/png这么写,明白吧,就不能有第二种写法,就必须这样写啊,当然了,如果你工作的时候,你不是输出png,你输出的是jpg,你就搜一下jpg,明白吧,你想输出什么,搜一下,写这句话啊,那现在呢,我们输出的是png啊,把image/png这句话呢,复制一下,然后呢,我再回到,MainServlet啊,那么ContentType里面,就要写image/png这段话,res.setContentType("image/png");

  • 那格式声明完以后,我们要输出了,那我们要把内容输出给浏览器,我们得获得一个输出流对吧,这个流不能自己创建,因为我们不知道浏览器是谁对吧,但服务器知道,我们得跟服务器要,那以前呢,我们输出的都是网页,都是文本,我们要的是一个字符流,对吧,getWriter,现在输出的是字节,我们要一个字节流,也有啊,怎么做呢,response点get,你看哪个是,你看,它这里面有两个,两个流啊,get,一个是之前我们用的,这个getWriter对吧,能输出字符,现在想输出字节对吧,这个getOutputStream。
    在这里插入图片描述

  • OutputStream os = res.getOutputStream();,我们就得到了一个输出流啊,这写个注释吧,就是获得,获取啊,这个字节输出流啊,该流由服务器创建,那么其目标,就是当前访问的那个浏览器。就总之吧,我们在做的当中啊,你想输出字符,getWriter,你想输出字节,getOutputStream,明白吧,想输出什么,就获取对应的流啊,那得到这个流以后,怎么输出呢,输出的方式啊,和这个,咱们那个main方法中的测试代码是一样的,再看一下啊。

    BufferedImage img =(BufferedImage) objs[1];
    ImageIO.write(img, “png”, os);

  • 那我们得到流以后啊,想输出东西,可以调这个ImageIO对吧,它可以帮我们输出,就调它啊,我们回到代码中来,调ImageIO啊,输出内容,ImageIO,点write啊,调那个有3个参的write,调这个3个参数的write,其中呢,第2个参,是格式,输出的就是后缀png啊,然后呢,第3个参,输出流os,那第一个参数是那个图片,那这个图片是谁呢,就是我们之前得到数组中的第2个值对吧,所以在此之前还得得到那个值,我忘了啊,写一下,在这写一下啊,objs,下标为1啊,它的类型呢,是BufferedImage啊,是这样一个类型啊,这个图片BufferedImage。

  • 那咱们得到了输出流以后,我们从数组中呢,得到了第二个值,这个值呢是图片,把它转型一下,然后呢,ImageIO.write()时,传入图片,要输出的是这个内容,然后呢,输出的图片呢,后缀或者是格式啊,是png,然后用的流,是os,完了,最后呢,别忘了要怎么样呢,把流关闭,os.close(),就可以了。那这个代码写完以后啊,咱们可以先测试一下,怎么测试呢,就是这个请求,谁访问它,/createImg.do,它会把图片呢,输出给谁,我直接打开浏览器访问它,它就把图片输出给浏览器,明白吧,不是网页,是图片,那我在网页上,用img来访问它,/createImg.do,它会把这个图片输出给img,明白吧,就总之呢,谁访问它,它会把这个图片输出给谁,先测一下。

  • 那我们先把这个项目呢,重新部署一下,部属以后啊,启动tomcat,然后呢,我们地址栏敲个地址,直接访问这个方法,它会把这个图片直接输出给浏览器,我们看一下。叫localhost:8080/netctoss/createImg.do,经过测试发现,可以得到图片对吧,这回输出的不是网页,是图片,那么服务器啊,有能力输出一切内容,只要你能写出来,它就能输出,输出什么都行,那我们不能这样用啊,我们得在那个登录页面上去得到这个图片,所以呢,我们再回到eclipse当中,然后呢,我们打开这个项目之下的登录页面,login.jsp,那么在login.jsp之上,我们找到那个验证码图片所处的位置,大概27行,是这样吧,之前呢,图片的路径是valicode.jpg,对吧,是固定的,现在我们把它改为动态的,那么改为谁呢,createImg.do,即<td><img src="images/valicode.jpg" alt="验证码" title="点击更换" /></td>,就是把这个图片呢,资源由静态的改为动态的。

  • 那么处理完以后呢,我们打开浏览器,我们访问这个登录功能看一下,看行不行啊,看一下,那么登录页面的访问路径啊,是toLogin.do,即localhost:8080/netctoss/toLogin.do,有么,有吧,行不行啊,可以。对,这个有的时候验证码看不清得能换对吧,是吧,点击更换,现在点击更换不了,为啥呢,因为我没写单击事件对吧,所以,肯定得写个单击事件,单击时把它换了,那我们回到程序当中,在这个img上,再写个单击事件,onclick="",27行啊,还是这个图片,在这事件里面呢,我直接写一句js,我要把这个图片换了,那大家想啊,我要换图片,实际上是要改什么才能让图片换,我得改点什么,就我通过改什么,能让图片有所变化,改样式,嗯,有人刷新网页,刷新网页就完了,因为啥呢,用户可能填了账号密码对吧,你把网页一刷新,账号密码没了,对吧,这就不太合适了,其实你应该能猜到,如果我把这个路径换了,它是不是就会换呢,是这意思吧,就是src路径,你换了,是不是就变了呢,是吧,你把这个值换了,就可以。

  • 那我想写js啊,去修改这个src,如果修改src,我得怎么改,我用什么方法去改,location啊,那还是改地址么,那不又刷新了么,你看啊,我们改的是元素的东西对吧,是吧,那肯定是dom操作,是这样吧,dom操作呢,我们以前讲了,API讲了什么呢,读写,查询,增删对吧,那这里是什么呢,是读写中的写对吧,是写,那修改就是修改,修改的话,我们可以改什么呢,改这个元素的内容,可以改元素的值,可以改元素的属性,是吧,这里是属于哪一个呢,改属性,改属性有几个办法呢,第一种办法setAttribute,是这样吧,第2种办法,点属性名对吧,但点属性名,很多是不标准的对吧,不好用,最好是setAttribute,所以这里呢,用setAttribute去改这个src。那我要setAttribute的话,我得先得到这个图片对吧,得到图片的话,怎么获得呢,你得加个id,然后document.getElementById对吧,然后再setAttribute,可以,但还有一种方式啊,你可以写什么呢,this,你看,我在这个图片上写this,这个this代表这个图片本身对吧,是吧,我可以这样写么,都忘了啊,<td><img src="createImg.do" onclick="this.setAttribute('src','createImg.do');" alt="验证码" title="点击更换" /></td>

  • 那this啊,得到这个图片,然后呢,改属性啊,setAttribute,属性名src,属性值,还是那句话,createImg.do,还是这句话。写完以后试一下,看行不行啊,我们看我这个,刷新一下,行吗,你们那个行,我这个真不行,这跟浏览器有关系啊,有些浏览器啊,它认为什么呢,你这属性是改了,你改完之后,这值和以前一样对吧,它认为一样,没变,它没去访问,有些浏览器就访问了,它没管啊,那这怎么办呢,我们得变通一下,让任何版本的浏览器都能变,怎么办呢,我,我善意的欺骗一下浏览器,我把这个路径伪装一下,让这浏览器以外它是变了,怎么伪装呢,我们这样做啊,你看一下,'createImg.do?x='+Math.random(),你看,我在路径之后,人为的加了个参对吧,瞎传个参,对,你说的太对了,瞎传个参,这参数什么什么,叫什么,无所谓,参数值只要每次都变就行,那浏览器以为,这路径变了,我就可以访问了,明白这意思么,听明白了么,我随便加个参,这参数随机数,只是为了欺骗浏览器,让它以为路径变了,听懂了吧,这个意思。

  • 你把它写上看一下,<td><img src="createImg.do" onclick="this.setAttribute('src','createImg.do?x='+Math.random());"alt="验证码" title="点击更换" /></td>,这样就万能了,什么浏览器都可以了,就是在路径之后啊,人为的拼个参数,参数名叫什么,没关系,等于一个随机数,每次都变就行,服务器是不需要这个参数的,对吧,这个参数只是为了欺骗浏览器的,没有别的作用啊,写完以后啊,试一下啊,你看我这个,刚才不好使,这回呢,这回就行了啊。那咱们第二个请求啊,加载个动态组件就搞定了,而第3个请求就容易了,坚持验证码对不对,判断就可以了啊。
    在这里插入图片描述

  • 那验证码的功能,在打开登录页面时,能够看到了,就是这个验证码好了,能看到了,但是还差一步啊,第3步,就是我们在登录时啊,还得对这个验证码进行检查,我们再把这个也写一下,把它完善了。那么要想检查验证码呢,首先呢,我们需要呢,在页面上,给这个框加上名字对吧,好让数据能传过去,然后呢,MainServlet.login()里,我们得到这个参数一比较就可以了啊,所以啊,首先我们打开那个login.jsp,那么,26行,这个验证码的框,得有name,这个name呢,我给它取名叫code吧,<td class="width70"><input name="code" type="text" class="width70" /></td>,取完名以后啊,我们再回到这个Servlet啊,打开这个MainServlet啊,找到login()方法。

  • 那么login方法里头呢,之前咱们是对账号密码进行了检查,那现在呢,这个验证码也需要呢,进行处理,所以呢,在这个方法的一开始,我们再接收一个参数啊,再多接收一个参数啊,是code,即String code = req.getParameter("code");,然后呢,接收到code以后啊,我们需要呢,将这个code和session中的code相比,所以呢,下面我们需要呢,获取session中的code,那这个比较,我们在哪比呢,我们在这个账号密码的检查之前,去比就可以,因为如果验证码不对的话,我们就没有必要查询了,是这样么,这样的话,这效率能,就是,能节约点效率,能提高点效率啊,所以我们在那个,在验证之前啊,在这优先呢,这个检查验证码,在这验证之前写,那我们需要呢,将得到的验证码和session中的相比,所以在这呢,我们还得获取session啊,但是我记得啊,咱们之前取过session,在这个方法里,我们在后面,在else里,咱们之前不是获取过session么,是吧,取过一次,我们把这个往前提一下,是吧,就可以了,就没必要再写一遍。

  • 因此呢,我把这个else啊,获取session这句话,把它剪切一下,啊,剪切完以后呢,把它往前提啊,那放到呢,这个方法的一开头这个地方,检查验证码这个位置,那提到这个位置以后呢,咱们从session中呢,得到这个正确的验证码啊,String imgcode = (String) session.getAttribute("imgcode"),我们将我们接收到的code啊,和这个session中的img相比,加以判断,那么如果呢,用户传入的code,是空的,或者呢,它不等于这个imgcode,它是错误的情况,需要给予提示,这个如果啊,code是空值,或者code不等于session的code,那么错误了,错误的处理方式和账号密码错误没什么区别,我们也是啊,要传一个这个错误的信息,要转发,所以呢,我们从这个账号,这个错误的地方呢,把这两句处理呢,复制一下,粘贴到上面去,然后稍微一修改就可以了。

    req.setAttribute(“error”, “账号错误”);
    req.getRequestDispatcher(“WEB-INF/main/login.jsp”).forward(req, res);

  • 那复制完这两句话以后啊,粘贴到刚才的判断之内,那么,需要修改的呢,是这个错误信息啊,那如果说呢,这个验证码不对,那大家想一想,我们后面账号密码的检查,还用执行么,后面不用执行了,所以程序到此,就怎么样呢,就结束了,我们在这就return啊,这就处理完了,处理完以后呢,咱可以测试了啊,


//检查验证码
HttpSession session = req.getSession();
String imgcode	= (String) session.getAttribute("imgcode");
if(code == null || !code.equalsIgnoreCase(imgcode)) {
	req.setAttribute("error", "验证码错误");
	req.getRequestDispatcher("WEB-INF/main/login.jsp").forward(req, res);
	return;
}

  • 我把这项目呢,再重新部署一下,然后呢,启动tomcat,然后呢,打开浏览器啊,我们再登录啊,重新登录,你看,我输入呢,正确的,账号,正确密码,然后呢,输入一个错误的验证码,那么,当我点登录的时候呢,它会给我提示啊,说验证码错误,那我填一个对的,填的时候呢,不用去看大小写啊,我这是fgnu啊,然后你看你自己的,然后呢,点登录,就成功了啊。那现在呢,我们就把这个验证码验证的功能说完了啊,那么,我们就把这个cookie啊,还有session,这两个对象,它们的应用讲完了,那么这个话题说我以后啊,我们再往下啊,说下一个话题啊。

验证码功能代码实现

1.src/main/java/util/ImageUtil.java
package util;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.imageio.ImageIO;

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

public final class ImageUtil {
	
	// 验证码字符集
	private static final char[] chars = { '0', '1', '2', '3', '4', '5', '6',
			'7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I' };
	// 字符数量
	private static final int SIZE = 4;
	// 干扰线数量
	private static final int LINES = 5;
	// 宽度
	private static final int WIDTH = 80;
	// 高度
	private static final int HEIGHT = 40;
	// 字体大小
	private static final int FONT_SIZE = 30;

	/**
	 * 生成随机验证码及图片
	 */
//	public static Map<String, BufferedImage> createImage() {
	public static Object[] createImage() {
		StringBuffer sb = new StringBuffer();
		// 1.创建空白图片
		BufferedImage image = new BufferedImage(WIDTH, HEIGHT,
				BufferedImage.TYPE_INT_RGB);
		// 2.获取图片画笔
		Graphics graphic = image.getGraphics();
		// 3.设置画笔颜色
		graphic.setColor(Color.LIGHT_GRAY);
		// 4.绘制矩形背景
		graphic.fillRect(0, 0, WIDTH, HEIGHT);
		// 5.画随机字符
		Random ran = new Random();
//		for (int i = 1; i <= SIZE; i++) {
		for (int i = 0; i < SIZE; i++) {
			// 取随机字符索引
//			int r = ran.nextInt(chars.length);
			int n = ran.nextInt(chars.length);
			// 设置随机颜色
			graphic.setColor(getRandomColor());
			// 设置字体大小
			graphic.setFont(new Font(null, Font.BOLD + Font.ITALIC, FONT_SIZE));
			// 画字符
//		graphic.drawString(chars[r] + "", (i - 1) * WIDTH / SIZE, HEIGHT / 2);
			graphic.drawString(chars[n] + "", i * WIDTH / SIZE, HEIGHT / 2);
			// 记录字符
//			sb.append(chars[r]);// 将字符保存,存入Session
			sb.append(chars[n]);// 将字符保存,存入Session
		}
		// 6.画干扰线
//		for (int i = 1; i <= LINES; i++) {
		for (int i = 0; i < LINES; i++) {
			// 设置随机颜色
			graphic.setColor(getRandomColor());
			// 随机画线
			graphic.drawLine(ran.nextInt(WIDTH), ran.nextInt(HEIGHT),
					ran.nextInt(WIDTH), ran.nextInt(HEIGHT));
		}
		// 7.返回验证码和图片
//		Map<String, BufferedImage> map = new HashMap<String, BufferedImage>();
//		map.put(sb.toString(), image);
//		return map;
		return new Object[] {sb.toString(), image};
	}

	/**
	 * 取随机色
	 */
	public static Color getRandomColor() {
		Random ran = new Random();
		Color color = new Color(ran.nextInt(256), ran.nextInt(256), ran.nextInt(256));
		return color;
	}
	
	//测试代码
	public static void main(String[] args) throws IOException {
		Object[] objs = createImage();
		BufferedImage image = (BufferedImage) objs[1];
		// /home/soft01/1.png
		OutputStream os = new FileOutputStream("d:/1.png");
		ImageIO.write(image, "png", os);
		System.out.println(objs[0]);
		os.close();
	}
	
	public static InputStream getInputStream(BufferedImage image)
			throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bos);
		encoder.encode(image);
		byte[] imageBts = bos.toByteArray();
		InputStream in = new ByteArrayInputStream(imageBts);
		return in;
	}
}
2.src/main/java/web/MainServlet.java
package web;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import dao.AdminDao;
import dao.CostDao;
import entity.Admin;
import entity.Cost;
import util.ImageUtil;

public class MainServlet extends HttpServlet {

	@Override
	protected void service(
		HttpServletRequest req, 
		HttpServletResponse res) throws ServletException, IOException {
		//获取访问路径
		String path = req.getServletPath();
		//根据规范(图)处理路径
		if("/findCost.do".equals(path)) {
			findCost(req,res);
		} else if("/toAddCost.do".equals(path)) {
			toAddCost(req,res);
		} else if("/addCost.do".equals(path)){
			addCost(req,res);
		} else if("/toLogin.do".equals(path)){
			toLogin(req,res);
		} else if("/toIndex.do".equals(path)){
			toIndex(req,res);
		} else if("/login.do".equals(path)){
			login(req,res);
		} else if("/createImg.do".equals(path)){
			createImg(req,res);
		} else {
			throw new RuntimeException("没有这个页面");
		}
	}
	
	//生成验证码
	protected void createImg(
		HttpServletRequest req, 
		HttpServletResponse res) throws ServletException, IOException {
		//生成验证码及图片
		Object[] objs = ImageUtil.createImage();
		//将验证码存入session
		HttpSession session = req.getSession();
		session.setAttribute("imgcode", objs[0]);
		//将图片发送给浏览器
		res.setContentType("image/png");
		//获取字节输出流,该流由服务器创建,
		//其目标就是当前访问的那个浏览器。
		OutputStream os = res.getOutputStream();
		BufferedImage img =(BufferedImage) objs[1];
		ImageIO.write(img, "png", os);
		os.close();
	}
	
	//登录
	protected void login(
		HttpServletRequest req, 
		HttpServletResponse res) throws ServletException, IOException {
		//接收参数
		String adminCode = req.getParameter("adminCode");
		String password = req.getParameter("password");
		String code = req.getParameter("code");
		System.out.println(code);
		//检查验证码
		HttpSession session = req.getSession();
		String imgcode	= (String) session.getAttribute("imgcode");
		System.out.println(imgcode);
		if(code == null || !code.equalsIgnoreCase(imgcode)) {
			req.setAttribute("error", "验证码错误");
			req.getRequestDispatcher("WEB-INF/main/login.jsp").forward(req, res);
			return;
		}
		//验证 
		AdminDao dao = new AdminDao();
		Admin a = dao.findByCode(adminCode);
		if(a == null) {
			//帐号错误 
			req.setAttribute("error", "账号错误");
			req.getRequestDispatcher("WEB-INF/main/login.jsp").forward(req, res);
		} else if(!a.getPassword().equals(password)) {
			System.out.println(password);
			System.out.println(adminCode);
			//密码错误
			req.setAttribute("error", "密码错误");
			req.getRequestDispatcher("WEB-INF/main/login.jsp").forward(req, res);
		} else {
			//将账号存入cookie
			Cookie cookie = new Cookie("user", adminCode);
			res.addCookie(cookie);
			//将账号存入session
//			HttpSession session = req.getSession();
			session.setAttribute("user", adminCode);
			//验证通过
			res.sendRedirect("toIndex.do");
		}
	}
	
	//打开登录页面
	protected void toLogin(
		HttpServletRequest req, 
		HttpServletResponse res) throws ServletException, IOException {
		req.getRequestDispatcher("WEB-INF/main/login.jsp").forward(req, res);
	}

	//打开主页
	protected void toIndex(
		HttpServletRequest req, 
		HttpServletResponse res) throws ServletException, IOException {
		req.getRequestDispatcher("WEB-INF/main/index.jsp").forward(req, res);
		}
	
	
	//查询资费
	protected void findCost(
		HttpServletRequest req, 
		HttpServletResponse res) throws ServletException, IOException {
		//查询所有的资费
		CostDao dao = new CostDao();
		List<Cost> list = dao.findAll();
		//将请求转发到jsp
		req.setAttribute("costs", list);
		//当前:/netctoss/findCost.do
		//目标:/netctoss/WEB-INF/cost/find.jsp
		req.getRequestDispatcher("WEB-INF/cost/find.jsp").forward(req, res);
	}

	//打开增加资费
	protected void toAddCost(
		HttpServletRequest req, 
		HttpServletResponse res) throws ServletException, IOException {
		req.getRequestDispatcher("WEB-INF/cost/add.jsp").forward(req, res);
	}
	
	//增加资费数据
	protected void addCost(
		HttpServletRequest req, 
		HttpServletResponse res) throws ServletException, IOException {
		//接收传入的参数
		req.setCharacterEncoding("utf-8");
		String name = req.getParameter("name");
		String costType = req.getParameter("costType");
		String baseDuration = req.getParameter("baseDuration");
		String baseCost = req.getParameter("baseCost");
		String unitCost = req.getParameter("unitCost");
		String descr = req.getParameter("descr");
		//保存该数据
		Cost c = new Cost();
		c.setName(name);
		c.setCostType(costType);
		if(baseDuration != null && baseDuration.length()>0){
			c.setBaseDuration(Integer.valueOf(baseDuration));
		}
		if(baseCost != null && baseCost.length()>0) {
			c.setBaseCost(Double.valueOf(baseCost));
		}
		if(unitCost != null && unitCost.length()>0) {
			c.setUnitCost(Double.valueOf(unitCost));
		}
		c.setDescr(descr);
		CostDao dao = new CostDao();
		dao.save(c);
		//重定向到查询
		//当前:/netctoss/addCost.do
		//目标:/netctoss/findCost.do
		res.sendRedirect("findCost.do");	
	}	
}
3.src/main/webapp/WEB-INF/main/login.jsp
<%@page pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>达内-NetCTOSS</title>
        <link type="text/css" rel="stylesheet" media="all" href="styles/global.css" />
        <link type="text/css" rel="stylesheet" media="all" href="styles/global_color.css" /> 
    </head>
    <body class="index">
        <div class="login_box">
        	<form action="login.do"  method="post">
	           <table>
	               <tr>
	                   <td class="login_info">账号:</td>
	                   <td colspan="2"><input name="adminCode" value="${param.adminCode }"  type="text" class="width150" /></td>
	                   <td class="login_error_info"><span class="required">30长度的字母、数字和下划线</span></td>
	               </tr>
	               <tr>
	                   <td class="login_info">密码:</td>
	                   <td colspan="2"><input name="password"  value="${param.password }"  type="password" class="width150" /></td>
	                   <td><span class="required">30长度的字母、数字和下划线</span></td>
	               </tr>
	               <tr>
	                   <td class="login_info">验证码:</td>
	                   <td class="width70"><input name="code" type="text" class="width70" /></td>
	                   <!-- <td><img src="images/valicode.jpg" alt="验证码" title="点击更换" /></td> -->
	                   <td><img src="createImg.do" onclick="this.setAttribute('src','createImg.do?x='+Math.random());" alt="验证码" title="点击更换" /></td>  
	                   
	                   <td><span class="required">验证码错误</span></td>              
	               </tr>            
	               <tr>
	                   <td></td>
	                   <td class="login_button" colspan="2">
	                       <!-- 
	                       		表单提交的方式:
	                       		1.点击submit按钮,触发表单的onsubmit事件
	                       		2.通过js调用表单的submit()
	                        -->	                   
	                       <a href="javascript:document.forms[0].submit();"><img src="images/login_btn.png" /></a>
	                   </td>    
	                   <!-- <td><span class="required">用户名或密码错误,请重试</span></td> -->
	                   <td><span class="required">${error }</span></td>                
	               </tr>
	           </table>
	    	</form>
        </div>
    </body>
</html>

写在后面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值