您的位置:首页 > Web前端 > JavaScript

WEB-11-JSP概述-1-会话/cookie/session/Ajax-记住密码/登录

2018-04-04 11:36 411 查看
 

1. JSP技术01

1.1. JSP概述

1.1.1. JSP概述

Servlet是j2ee提供的动态资源开发技术,是以java的程序的形式进行开发,在java中书写HTML标签是一件十分头疼的事情,所以人们开发出了JSP,看起来像是HTML一样,但可以在其中写java代码,本质上是动态web资源,Jsp会在第一次访问时被容器翻译为Servlet。

1.2. JSP基本语法

1.2.1. 模版元素

直接书写在JSP中的HTML内容,看起来就像写HTML一样的方便,但是最终会在被翻译成Servlet的过程中out.write()直接输出

1.2.2. 脚本表达式

<%= 表达式%>接受的是一段java表达式,在JSP翻译到Servlet的过程中,将会计算表达式的值,利用out.write()输出出去

1.2.3. 脚本片段

<%  %>直接可以在脚本片段中书写java源代码,其中的源代码将会直接拷贝到翻译过来的servlet中的响应位置上。

1.2.4. JSP声明

<%! %>在其中可以写java代码,其中的源代码会被拷贝到servlet中的service方法之外,可以利用它来为servlet增加成员方法、成员变量、静态代码块

1.2.5. JSP注释

<%-- --%>被jsp注释包围起来的内容将不会被服务器翻译到servlet之中,要注意区分html注释和java注释的区别

jsp注释不会被翻译到servlet中,会在翻译时遗弃

java注释不会被编译到class文件中,会在编译时遗弃

html注释将会当作模版元素,直接输出到浏览器中,浏览器将不会显示html注释中的内容

2. 会话技术

2.1. 会话技术

2.1.1. 会话概述

会话可简单理解为:用户开一个浏览器,访问多个资源,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。

2.1.2. 会话中的问题

每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,程序要想办法为每个用户保存这些数据。

如用户登录过后,应该保存一个用户状态,在会话结束前表明用户一直是登录状态。并且不同的用户之间的登录状态应该互不影响。

我们之前已经学过request域和SerlvetContext域,我们分别分析一下。

Request域太小了,在多次请求中,每次请求响应都是新的Request对象,之前存入的request域中的域属性,在请求结束后随着request域的销毁而消失了。

ServletContext域太大了,所有用户都在操作这个域,必然会发生混乱。

此时我们需要会话相关的技术,保存会话产生的数据。

3. Session技术

3.1. Session技术

3.1.1. Session概述

Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,
4000
可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务。

如图-1所示:

 


图-1

3.1.2. session是一个域对象

生命周期:

当程序第一次调用到request.getSession()代码时,服务器明确的直到了需要用到session了,此时创建session.

如果session超过30分钟(可以在web.xml中配置的)没人使用,服务器认为这个session超时了,销毁session.

明确的调用session.invalidate(),session立即销毁.

服务器被非正常关闭或web应用被移除出容器,此时随着web应用的销毁session销毁.如果是正常关闭,session会被钝化.当下次服务器正常启动时,没有超时的session还会被活化回来.

作用范围:

整个会话范围内可见

主要作用:

在会话范围内共享数据

3.2. 利用Session实现EasyMall中的登录功能

3.2.1. 登录的原理

所谓的登录,就是要将登录过后的用户状态保存起来,在后续的请求中能够获知这种状态,这就是典型的在会话中保存数据的过程。

我们可以在Session中保存一个登录标记,表明当前用户已经登录过。后续需要时只需检查Session中的该标记即可判断出用户是否登录过。

3.2.2. 登录代码实现

首先我们导入用户登录页面,如图-2所示:

 


图-2
创建登录用的Servlet,将登录用表单的地址设置为该Servlet,如图-3所示:

 


图-3
在Servlet我们获取用户提交的用户名密码

//解决请求乱码

request.setCharacterEncoding("utf-8");

//解决响应乱码

response.setContentType("text/html;charset=utf-8");

 

//获取用户提交的用户名 密码

String username = request.getParameter("username");

String password = request.getParameter("password");

通过查询数据库检查用户名密码是否正确,如果不正确则进行提示,如果正确则在Session中保存用户的登录状态后回到主页

//查询数据库是否存在指定用户名密码的用户

try {

SAXReader reader = new SAXReader();

Document dom = reader.read(this.getServletContext().getRealPath("WEB-INF/classes/users.xml"));

Element root = dom.getRootElement();

Element ele = (Element) root.selectSingleNode("//user[@username='"+username+"' and @password='"+password+"']");

if(ele == null){

//没有找到,提示用户名密码不正确

response.getWriter().write("用户名密码不正确!");

return;

}else{

//用户名密码正确,将用户名保存到session中表明用户登录过

request.getSession().setAttribute("user", username);

//回到主页

response.sendRedirect("/index.jsp");

}

} catch (Exception e) {

e.printStackTrace();

throw new RuntimeException(e);

}

3.3. Session实现防止表单重复提交

3.3.1. 表单重复提交概述

当网络延迟等情况发生时,用户无法确定表单是否已经提交,从而多次点击提交,造成数据多次提交,称为发生了表单重复提交。

真实案例:12306网站购票

3.3.2. 防止表单重复提交

生成一个令牌。

在session域中保存令牌。

在表单中隐藏字段保存令牌。

当表单提交时,在处理的Servlet里检查,如果提交的令牌和session中保存的令牌一致,则执行逻辑,并删除session中的令牌。

如果session域中没有令牌,或和提交的令牌不符合,则认为是表单重复提交,提示。

3.3.3. EasyMall注册用户防止表单重复提交

在regist.jsp中设置代码保存令牌—我们此处用随机数作为令牌。如图-4所示:

 


图-4
在RegistServlet中检查,如图-5所示:

 


图-5

3.4. Session实现验证码

验证码提交后该如何进行校验呢?应该在用户获取验证码时,将验证码信息存入session域,在用户提交表单时,将用户提交的验证码和session中保存的验证码进行比较,如果一致,则验证码正确。

ValiImgServlet,如图-6所示:

 


图-6
RegistServlet,如图-7所示:

 


图-7
regist.jsp,如图-8所示:

 


图-8

4. Cookie技术

4.1. Cookie技术

4.1.1. Cookie概述

Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。如图-9所示:

 


图-9
cookie是会话相关的技术,可以保存会话产生的数据,客户端技术,将数据保存在客户端,基于set-Cookie响应头和Cookie请求头工作的.

4.1.2. Cookie常用方法

Cookie cookie = new Cookie(String name,String value);

--javax.servlet.http.Cookie类用于创建一个Cookie

 

setValue与getValue方法

--设置和获取cookie的值

getName方法

--cookie需要在创建时就指定好名字,这个名字一旦指定就不能修改了,如果需
1b5d8
要修改只能重新创建一个cookie

 

setMaxAge与getMaxAge方法  

--一个cookie被发送到浏览器时,如果没有设置过MaxAge,这个cookie将会被保存在浏览器的内存中,会一直存在到浏览器关闭内存销毁为止,这样的cookie叫做会话级别的Cookie

--我们也可以使用setMaxAge方法设置cookie的存活时间,这样一来,浏览器收到这个Cookie信息时会将cookie信息以文件的形式保存在浏览器的临时文件夹中,保存到指定的时间到来位置,在这个期间即使多次开关浏览器,cookie信息一直都在.

 

setPath与getPath方法

--设置浏览器在访问哪个地址及其子地址时,带着cookie信息过来.如果没设置过,那么默认的path是当前发送cookie的Servlet所在的路径

 

setDomain与getDomain方法

--设置浏览器在访问哪个域名时,带着当前的cookie,默认值当前发送cookie的域名,注意,现代的浏览器只要发现cookie设置过maxage,则认为这个cookie是第三方cookie,会拒绝接受

 
 

4.1.3. 发送cookie

response.addCookie(cookie);

--response接口也中定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段。

4.1.4. 获取cookie

request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。

Cookie [] cs = request.getCookies();

4.1.5. 删除cookie

浏览器会通过名字+path+domaint来区分cookie,如果浏览器发现两个cookie名字path
domain 都相同,则会认为是相同的cookie,就会发送cookie的覆盖。

所以如果想要删除一个cookie,可以发送一个和要删除的cookie名字path (domain)都相同的cookie,然后将这个cookie的maxAge设置为0,这样一来,这个cookie会把要删除的cookie覆盖掉,覆盖后立即超时,直接被浏览器删除,看起来就像是删除了要删除的cookie

4.1.6. Cookie细节

一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。

一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。

浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。

如果创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie。

注意,删除cookie时,path必须一致,否则不会删除(浏览器通过cookie的name+path来标识一个cookie)

4.2. EasyMall实现记住用户名功能

4.2.1. 记住用户名功能分析

登录页面如图-10所示:

 


图-10
如果用户在登录时勾选了记住用户名,则应该将用户名称用户名保存起来,在用户下次登录时,读取保存的用户名,显示出来。

如果用Session保存,我们知道,Session一旦30分钟不使用就会失效,这个时间显然太短了。

而用Cookie保存,可以设置MaxAge指定保存的时常,直到指定时间结束或用户手动清楚cookie之前,此信息一直存在。

所以我们选择Cookie来实现保存用户名的功能。

4.2.2. 代码实现

在用户登录的Servlet中,当用户登录成功后,判断用户是否勾选过记住用户名,如果用户勾选过,则发送cookie保存用户名,注意MaxAge设置为30天。

//查询数据库是否存在指定用户名密码的用户

try {

SAXReader reader = new SAXReader();

Document dom = reader.read(this.getServletContext().getRealPath("WEB-INF/classes/users.xml"));

Element root = dom.getRootElement();

Element ele = (Element) root.selectSingleNode(

"//user[@username='"+username+"' and @password='"+password+"']"

);

if(ele == null){

//没有找到,提示用户名密码不正确

response.getWriter().write("用户名密码不正确!");

return;

}else{

//用户名密码正确,将用户名保存到session中表明用户登录过

request.getSession().setAttribute("user", username);

//检查用户是否勾选过记住用户名

if("true".equals(request.getParameter("remname"))){

//发送cookie保存用户名

Cookie remnamec = new Cookie("remname",username);

remnamec.setPath("/");

remnamec.setMaxAge(3600*24*30);

response.addCookie(remnamec);

}

//回到主页

response.sendRedirect("/index.jsp");

}

} catch (Exception e) {

e.printStackTrace();

throw new RuntimeException(e);

}

经过测试,发现当用户勾选过记住用户名时,会发送cookie给浏览器保存用户名。如图-11所示:

 


图-11
当浏览器再次访问登录页面时,请求中带了cookie,其中保存着用户名。如图-12所示:

 


图-12
现在我们只需要在登录页面,读取到cookie中保存的用户名显示到用户名输入项即可。

我们当前输出登录页面用的是html页面,html是静态web资源无法在其中写代码读取cookie数据,所以我们改造为用jsp实现。

将login.html改为login.jsp,在其中嵌入java代码,读取请求中的cookie信息,填入用户名输入框。如图-13所示:

 


图-13
经测试,可以正确的显示用户名,如图-14所示:

 


图-14
上面的测试中我们用的用户名是英文的,再测试一下中文。如图-15所示:

 


图-15
提交后发现报错,如图-16所示:

 


图-16
为什么会报错呢?我们知道cookie是基于HTTP协议中的set-Cookie请求头和Cookie响应头工作的。而HTTP协议本身只支持ISO8859-1编码,ISO8859-1中没有中文,所以报错了。

那么该如何解决呢?

Web开发中有一种常用的编码方式叫URL编码就是用来解决这种问题的。

5. URL编码

5.1. URL编码

5.1.1. URL编码概述

其实这种编码方式我们见过,当我们用浏览器提交中文数据时,中文数据也是经过URL编码后提交的。对于非iso8859-1的字符,浏览器和服务器默认使用URL编码进行处理。如图-16所示:

 


图-16

5.1.2. URL编码原理



5.1.3. URL编解码

Java中提供了进行URL编码和解码的类

URLEncoder:static String encode(String s,String enc);

//将指定的字符串按照指定的编码转换为URL编码的形式

URLDecoder:static String decode(String s,String enc);

//将URL编码后的字符串按照指定编码解码为源字符串

5.2. EasyMall记住用户名中文处理

5.2.1. EasyMall记住用户名中文处理

我们可以通过URL编码解决集中用户名中的中文问题。

改造代码,在发送cookie时,进行URL编码

//检查用户是否勾选过记住用户名

if("true".equals(request.getParameter("remname"))){

//发送cookie保存用户名

Cookie remnamec =

new Cookie("remname",URLEncoder.encode(username, "utf-8"));

remnamec.setPath("/");

remnamec.setMaxAge(3600*24*30);

response.addCookie(remnamec);

}

经测试cookie中保存的是进过URL编码的用户名。如图-17所示:

 


图-17
在login.jsp中,用户名要经过URL解码,如图-18所示:

 


图-18
经测试,可以正常的处理中文用户名。如图-19所示:

 


图-19

6. session的原理

6.1. Session原理

6.1.1. Session原理

服务器是如何区别出当前请求是要对应到哪个Sessoin的呢?其实Session是基于一个特殊的名为JSESSIONID的cookie工作的。如图-20所示:



图-20

6.2. 利用Session原理实现即使多浏览器共用Session

6.2.1. 代码实现

利用sesison的原理,我们可以自己写出一个JSESSIONID cookie,设置maxAge,从而实现即使关闭浏览器仍能找到之前的session

 


图-21

6.3. 禁用cookie时使用session

6.3.1. 禁用cookie时使用session

当客户端禁用了cookie时,造成session无法访问,此时我们可以使用URL重写来解决这个问题

URL重写要求将站点中的所有超链接都进行改造,在超链接后用一个特殊的参数JSESSIONID保存当前浏览器对应session的编号,这样一来,当用户点击超链接反问服务器时,服务器可以从URL后的参数中分析出JSESSIONID,从而找到对应的sesison使用.

URL重写之前,要先创建出session,才能进行重写操作

response.encodeURL(String url);

//--如果是普通的地址用这个方法

response.encodeRedirectURL(String url);

//--如果地址是用来进行重定向的,用这个方法

以上两个方法可以实现URL重写,这两个方法非常的智能,只要发现浏览器发送了任何Cookie过来,就认为当前客户端没有禁用Cookie,就不会在进行URL重写

7. JSP技术02

7.1. JSP指令

7.1.1. page指令

用来通知翻译引擎,如何翻译当前的JSP

[ language="java" ] 当前JSP使用的开发语言

[ extends="package.class" ] 当前jsp翻译成servlet后要继承的类,注意此值必须是一个servlet的子类,一般情况下不要改

[ import="{package.class | package.*}, ..." ]
导入需要使用到的包 java.lang.*;javax.servlet.*;javax.servlet.jsp.*;javax.servlet.http.*;

[ session="true | false" ] 用来指定当前页面是否使用session,如果设置为true,则翻译过来的servlet中将会有对session对象的引用,于是可以直接在jsp中使用session隐式对象。但是这将导致一旦访问jsp就会调用request.getSession()方法,可能导致不必要的空间浪费。如果确定jsp中不需要session可以设为false

[ buffer="none | 8kb | sizekb" ] out隐式对象所使用的缓冲区的大小

[ autoFlush="true | false" ] out隐式对象是否自动刷新缓冲区,默认为true,不需要更改

[ isThreadSafe="true | false" ] 翻译过来的servlet是否实现SingleThreadModel

[ errorPage="relative_url" ] 如果页面出错,将要跳转到的页面,除了在jsp中使用此属性指定错误页面外也可以在web.xml中配置整个web应用的错误页面,如果两个都设置则jsp中的此属性起作用

[ isErrorPage="true | false" ] 如果设置此属性为true,翻译过来的servlet中将含有Exception隐式对象,其中封装的就是上一个页面中抛出的异常对象

[ contentType="mimeType [ ;charset=characterSet ]" | "text/html ; charset=ISO-8859-1" ]和jsp乱码相关的指令,用来指定jsp输出时,设置的Content-Type响应头用来指定浏览器打开的编码

[ pageEncoding="characterSet | ISO-8859-1" ]
服务器翻译jsp时使用的编码集.如果向防止jsp乱码,应该保证文件的保存编码和jsp翻译成servlet用的编码以及输出到浏览器后浏览器打开的编码一致.此属性一旦设置好,翻译引擎会间接帮我们设置content-type属性.

[ isELIgnored="true | false" ] 当前页面是否使用el表达式,设置为false时表示启用el,j2ee4.0开始默认支持,j2ee4.0一下做开发时,如果要使用el表达式,需将此属性设置为fals

 

7.1.2. include指令

<%@ incluede file=""%> 静态引入其他页面的内容

*静态引入:

在源文件级别进行合并,多个jsp生成一个servlet,最终由这一个servlet生成响应。推荐使用。

*动态引入:

在运行时将多个输出进行合并,多个jsp分别生成多个servlet,最终由这多个servlet生成响应,组成一个输出流,提供响应。执行效率没有静态引入高。

7.1.3. taglib指令

<%@ taglib uri="" prefix=""%>用来引入标签库。

uri指定被引入.tld文件的名称空间

prefix 对该名称空间的一个缩写

7.2. 九大隐式对象

7.2.1. 九大隐式对象概述

在jsp翻译成Serlvet时,jsp翻译引擎在翻译过来的Serlvet中,预先定义了一些变量,可以不需要在JSP中预先定义就可以直接使用,共有9个,统称为jsp的九大隐式对象。分别是:

page

config

application

response

request

session

out

exception

pageContext

其中其他的七个我们都学习过了,只需要再学习一下out和pageContext即可。

7.2.2. out对象

可以将他理解成response.getWriter()获得的PrintWriter.

它自带一个缓冲区,其大小收page指令中的buffer的设定限制。当缓冲区满或缓冲区被关闭或当前jsp页面结束,则此缓冲区中的内容将被刷新到response.getWriter()的缓冲区中。

7.2.3. PageContext对象

(1)作为入口对象获取其它八大隐式对象。

getException方法返回exception隐式对象

getPage方法返回page隐式对象

getRequest方法返回request隐式对象

getResponse方法返回response隐式对象

getServletConfig方法返回config隐式对象

getServletContext方法返回application隐式对象

getSession方法返回session隐式对象

getOut方法返回out隐式对象

(2)作为入口对象获取其他域中的数据

pageContext操作所有域中属性的方法

getAttribute(String name,int scope)

setAttribute(String name, Object value,int scope)

removeAttribute(String name,int scope)

其中pageContext中代表域的常量:

PageContext.APPLICATION_SCOPE

PageContext.SESSION_SCOPE

PageContext.REQUEST_SCOPE

PageContext.PAGE_SCOPE

findAttribute方法:

在四大域中搜寻属性,搜寻的顺序是page域、request域、session域、application域,从小域到大域开始搜索,如果搜索到就直接获取该值,如果所有域中都找不到,返回一个null

(3)作为域对象使用

作用的范围:真个jsp页面,是四大作用域中最小的一个。

生命周期:当对jsp的请求开始时生成,当响应结束时销毁。

作用:在jsp页面范围内共享数据

 

(4)跳转到其他资源

其身上提供了forward和include方法,简化重定向和转发的操作

 

1. AJAX

1.1. AJAX详解

在学习HTTP协议的时候,我们知道,HTTP协议基于请求响应模型,一次请求对应一次响应,请求只能由客户端发出,服务器只能被动的等待请求。

但我们经常见到如下的功能:

网络聊天室里,聊天信息,不需要刷新页面就可以接受最新的聊天消息。

网站注册时,输入用户名后无需提交立即提示用户名已经存在。

百度输入搜索条件时,根据输入的不同提示不同的备选查询内容。

 


百度地图根据,鼠标的位置变化,显示内容变化

 


在这些应用场景中,我们没有刷新网页,却获取到了服务器发回的最新数据。这不是很明显的违反了HTTP协议了吗?

其实这不是违反了HTTP协议,而是使用了一种“另类”的技术,叫做AJAX(Asynchronous JavaScript and XML)。改变了传统方式的同步刷新方式,改为了异步刷新—即在保持当前页面的情况下,异步发出请求等待响应,再根据得到的响应消息修改当前页面。

Ajax并不是一项新技术,其实是多种技术的综合,包括Javascript、XHTML和CSS、DOM、XML和XMLHttpRequest。

XMLHttpRequest对象方法

 


XMLHttpRequest对象属性

 


想要实现Ajax需要如下几个步骤

//1 创建XMLHttpRequest对象
function ajaxFunction(){
   var xmlHttp;
   try{ // Firefox, Opera 8.0+, Safari
        xmlHttp=new XMLHttpRequest();
    }
    catch (e){
   try{// Internet Explorer
         xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
      }
    catch (e){
      try{
         xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
      }
      catch (e){}
      }
    }
return xmlHttp;
 }
 
//2.服务器端向客户端进行响应(注册监听)
var data = null;
var xhr = ajaxFunction();
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
if(xhr.status==200||xhr.status==304){
data = xhr.responseText;
}
}
}
 
 
readyState
属性表示Ajax请求的当前状态。它的值用数字代表。
0 代表未初始化。 还没有调用open方法
1 代表正在加载。open
方法已被调用,但send方法还没有被调用
2 代表已加载完毕。send已被调用。请求已经开始
3 代表交互中。服务器正在发送响应
4 代表完成。响应发送完毕

xhr.status
常用状态码及其含义:
404 没找到页面(not found)
403 禁止访问(forbidden)
500 内部服务器出错(internal service error)
200 一切正常(ok)
304 没有被修改(not modified)(服务器返回304状态,表示源文件没有被修改)

xhr.responseText
服务器发回的响应结果,字符串
xhr.responseXML
服务器返回的响应结果,XML对象
 
 
//3 客户端与服务器端建立连接
 * 使用的是XMLHttpRequest对象的open(method, url, asynch)方法
 * * method:请求类型,类似 “GET”或”POST”的字符串。
 * * url:路径字符串,指向你所请求的服务器上的那个文件。可以是绝对路径或相对路径。
 * * asynch:表示请求是否要异步传输,默认值为true(异步)。
xhr.open("GET","../testServlet?timeStamp="+new Date().getTime()+"&c=19",true);
 
//4 客户端向服务器端发送请求
 *
 * 使用的是XMLHttpRequest对象的send()方法
 * * 如果请求类型是GET方式的话,使用send()方法发送请求数据,服务器端接收不到
 * * 该步骤不能被省略,只能写成xhr.send(null);
 */
 
 xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");

~GET方式:
xhr.send(null); //xhr.send(null);

~POST方式:如果请求类型是POST的话,需要设置请求首部信息
xhr.send("a=7&b=8");

1.2.  AJAX实现检查用户名是否已经存在

开发检查用户名是否已经存在的Servlet

 


在用户输入用户名后,触发js代码,用AJAX技术将用户名提交到Servlet中检查用户名。

 


 

2. EasyMall实现验证码

验证码是为了防止用户恶意注册而准备的一些不易识别的图片,要求用户输入图片中的内容进行验证,从而防止恶意使用程序提交数据。这些验证码图片不宜使用固定的图片,而应该用程序动态生成。

package com.itheima.web;
 
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
 
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class ValiImgServlet extends HttpServlet {
 
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
int base = 30;
int height = base;
int width = base * 4;
BufferedImage img = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//获取画布
Graphics2D g = (Graphics2D) img.getGraphics();
//--填充背景色
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
//--画边框
g.setColor(Color.BLACK);
g.drawRect(0, 0, width-1, height-1);
//--写上验证码的值
String cs = "\u7684\u4e00\u4e86\u662f\u6211\u4e0d\u5728\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a\u7740\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u4f60\u90fd\u628a\u597d\u8fd8\u591a\u6ca1\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u4e94\u7b2c\u4f7f\u5199\u519b\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636\u8fde\u8f66\u91cd\u4fbf\u6597\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u65e5\u51b3\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be\u65e9\u8bba\u5417\u6839\u5171\u8ba9\u76f8\u7814\u4eca\u5176\u4e66\u5750\u63a5\u5e94\u5173\u4fe1\u89c9\u6b65\u53cd\u5904\u8bb0\u5c06\u5343\u627e\u4e89\u9886\u6216\u5e08\u7ed3\u5757\u8dd1\u8c01\u8349\u8d8a\u5b57\u52a0\u811a\u7d27\u7231\u7b49\u4e60\u9635\u6015\u6708\u9752\u534a\u706b\u6cd5\u9898\u5efa\u8d76\u4f4d\u5531\u6d77\u4e03\u5973\u4efb\u4ef6\u611f\u51c6\u5f20\u56e2\u5c4b\u79bb\u8272\u8138\u7247\u79d1\u5012\u775b\u5229\u4e16\u521a\u4e14\u7531\u9001\u5207\u661f\u5bfc\u665a\u8868\u591f\u6574\u8ba4\u54cd\u96ea\u6d41\u672a\u573a\u8be5\u5e76\u5e95\u6df1\u523b\u5e73\u4f1f\u5fd9\u63d0\u786e\u8fd1\u4eae\u8f7b\u8bb2\u519c\u53e4\u9ed1\u544a\u754c\u62c9\u540d\u5440\u571f\u6e05\u9633\u7167\u529e\u53f2\u6539\u5386\u8f6c\u753b\u9020\u5634\u6b64\u6cbb\u5317\u5fc5\u670d\u96e8\u7a7f\u5185\u8bc6\u9a8c\u4f20\u4e1a\u83dc\u722c\u7761\u5174\u5f62\u91cf\u54b1\u89c2\u82e6\u4f53\u4f17\u901a\u51b2\u5408\u7834\u53cb\u5ea6\u672f\u996d\u516c\u65c1\u623f\u6781\u5357\u67aa\u8bfb\u6c99\u5c81\u7ebf\u91ce\u575a\u7a7a\u6536\u7b97\u81f3\u653f\u57ce\u52b3\u843d\u94b1\u7279\u56f4\u5f1f\u80dc\u6559\u70ed\u5c55\u5305\u6b4c\u7c7b\u6e10\u5f3a\u6570\u4e61\u547c\u6027\u97f3\u7b54\u54e5\u9645\u65e7\u795e\u5ea7\u7ae0\u5e2e\u5566\u53d7\u7cfb\u4ee4\u8df3\u975e\u4f55\u725b\u53d6\u5165\u5cb8\u6562\u6389\u5ffd\u79cd\u88c5\u9876\u6025\u6797\u505c\u606f\u53e5\u533a\u8863\u822c\u62a5\u53f6\u538b\u6162\u53d4\u80cc\u7ec6";
StringBuffer buffer = new StringBuffer();
for(int i = 0;i<4;i++){
g.setColor(new Color(getRand(0, 255),getRand(0, 255),getRand(0, 255)));
g.setFont(new Font("宋体",Font.BOLD,18));
double rotate = getRand(-45, 45)*1.0/180*Math.PI;
g.rotate(rotate,5 + i * base, 22);
String s = cs.charAt(getRand(0,cs.length()))+"";
g.drawString(s, 5 + i * base, 22);
buffer.append(s);
g.rotate(-1*rotate,5 + i * base, 22);
}
//--画干扰线
for (int i = 1; i < 5; i++) {
g.setColor(Color.RED);
g.drawLine(getRand(0, width), getRand(0, height),
getRand(0, width), getRand(0, height));
}
//--画干扰点
for(int i = 1;i<8;i++){
g.setColor(new Color(getRand(0, 255),getRand(0, 255),getRand(0, 255)));
int r = getRand(2, 15);
g.drawOval(getRand(0, width), getRand(0, height), r,r);
}
//释放画布,将内存中的图片打给浏览器
g.dispose();
ImageIO.write(img, "jpg", response.getOutputStream());
}
 
private Random rand = new Random();
private int getRand(int begin,int end){
return rand.nextInt(end-begin)+begin;
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
修改注册页面验证码图片地址,指向该Servlet,经实验,可以正确显示验证码图片。

 


经过测试,多次访问该页面验证码图片每次都是相同的,这是因为浏览器有缓存机制,每次访问页面用的都是第一次验证码图片的缓存。验证码是不应该被缓存的,我们在学习HTTP协议的时候学习过控制缓存相关的响应头,可以在验证码Servlet中增加代码禁止浏览器的缓存,经过测试解决了验证码缓存的问题。

response.setDateHeader("Expires", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  WEB概述