一、会话
会话可简单的理解为:用户的打开一个浏览器,点击多个超链接访问 Web 服务器中的多个资源,然后关闭浏览器的过程,称为会话。
1、服务器与客户机数据交互问题
每个用户与服务器进行交互的过程中,各自都会有一些数据,程序要想办法保存每个用户的数据。
例如现在有一个购物网站,当用户购物的时候,我们需要将用户购买的商品记录下来,最后结账的时候为客户展现已购买的商品。在 Servlet 中有多种交互数据的方式。
(1) ServletContext 域保存用户数据。如果是这样的话,那么在整个当前 Web 应用范围内,这个用户的数据都是被共享的。假设 A 用户购买了商品 a ,此时 B 用户拿到了 ServletContext 中的 a 数据,进行了修改,那么当 A 用户转到结账页面的时候,发现商品已经被更改了。所以这种方法是不可行的。
(2) resquest 域保存用户数据。如果使用了 request 那么我们先看一下 request 的生命周期,每当用户向服务器发送一个请求的时候,服务器都会创建一个 request 来进行处理。如果 A 用户购买了某个商品,此时再点击超链接去结账的时候,又向服务器发送了一个请求,服务器又创建了一个全新的 request ,那么新创建的这个 request 中是不会存在 A 用户购买的商品信息的。也许你会想到用请求转发,当 A 用户购买了商品后,服务器将请求转发给结账的页面。这种方法在技术上是可行的,但在用户体验方面是不好的。因为每当用户点击购买一个商品的时候,服务器都会转发到结账页面,用户如果想购买 100 件商品后,那么就必须进行 100 次结账操作。所以 request 方式也是不可行的。
二、保存会话过程中数据的两种技术
1、Cookie ,Cookie 是客户端技术,程序把每个用户的数据以 cookie 的形式写给用户各自的浏览器,当用户使用浏览器去访问 Web 服务器资源时,就会带着各自的数据去。这样 Web 资源处理的就是用户各自的数据了。(用户各自拿着各自的数据)
(1) 浏览器向服务器发送请求,访问服务器的某个资源
(2) Servlet 以 Cookie 的形式回写给用户,让有用户自己保存数据
(3) 浏览器再访问服务器其他资源的时候,会带着 Cookie 去访问服务器,其他资源通过用户带过来的 Cookie 提取出数据,为用户服务
2、Session ,Session 是服务器端技术,利用这个技术,服务器在运行时可以为每个用户的浏览器创建一个独享的 session 对象,由于 session 是用户浏览器独享,所以每个用户在访问 Web 资源时,可以把各自的数据放在 session 中,当用户访问服务器的时候,服务器会取出各自的 session 为其服务。(服务器为每个用户分别保存数据)
(1) 浏览器向服务器发送请求,访问服务器的某个资源
(2) 服务器先运行 session = resquest.getSession() ,之后服务器发现如果还没有为这个用户创建 Session 对象,则为用户创建一个独享的 Session。
(3) 将用户的数据存放在这个独享的 Session 对象
(4) 当浏览器再访问其他服务器资源的时候,服务器通过 session = resquest.getSession() ,服务器发现已经存在了一个 Session 对象,那么服务器就会取出这个 Session 对象中的数据为用户服务
三、Cookie
1、常用 API
Cookie 的构造函数创建一个 Cookie,指定 Cookie 的名和 Cookie 的值。
向客户机输出 Cookie ,使用 response 中的 addCookie(Cookie cookie) 方法。
获取客户机 Cookie ,使用 resquest 中的 getCookies() 方法,返回的是一个 Cookie[] 数组。
setMaxAge 和 getMaxAge 方法是用于设置 Cookie 的有效期。如果没有显示的设置 Cookie 的有效期,那么这时的 Cookie 是一个会话级别的 Cookie,Cookie 会保存在浏览器的进程所对应的那快内存区域。当会话结束了以后, Cookie 就会被删除。
setPath 和 getPath ,如果设置了一个目录进去,那么当浏览器访问服务器这个目录下资源以及子目录的时候才会带着 Cookie 过去。如果没有设置 setPath 那么默认的目录为当前 Web 应用。
setDomain 和 getDomain ,这两个方法是设置 Cookie 的有效域,www.baidu.com 这个为一个主机名,.baidu.com 就是一个域。如果指定了域,那么访问某个特定域的时候才会带 Cooike 过去。第一方 Cookie :setDomain("") 的域与当前应用的域名一致,那么此时浏览器获取到的 Cookie 就是第一方 Cookie。第二方域:setDomain("") 的域与当前应用的域名不一致,那么此时浏览器获取到的 Cookie 就是第三方 Cookie。IE 浏览器统统阻止第三方 Cookie 。
2、编程实例:记录用户上次访问时间
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- Cookie[] cookies = request.getCookies();
- for (int i = 0; cookies != null && i < cookies.length; i++) {
- String name = cookies[i].getName();
- if(name.equals("lastvisted")){
- response.setContentType("text/html;charset=utf-8");
- PrintWriter writer = response.getWriter();
- String value = cookies[i].getValue();
- writer.write("您上次登录的时间是:" + value);
- }
- }
- Cookie cookie = new Cookie("lastvisted", new Date(System.currentTimeMillis()).toLocaleString());
- cookie.setMaxAge(30*24*60*60);
- response.addCookie(cookie);
- }
3、Cookie 细节
一个 Cooike 只能标识一种信息,它至少含有一个标识名称(NAME)和值(VALUE)。
一个 Web 站点可以给一个 Web 浏览器发送多个 Cookie,一个浏览器也可以存储多个 Web 站点提供的 Cookie。
浏览器一般只允许存放 300 个 Cookie,每个站点最多存放 20 个 Cookie,每个 Cookie 的大小限制为 4KB。
如果创建了一个 Cookie,并将它发送到浏览器,默认情况下它是一个会话级别的 Cookie,也就是存储在浏览器内存中。用户如果退出浏览器之后,Cookie 会被马上删除,如果希望浏览器将该 Cookie 存储在磁盘上,则需要使用 maxAge,并给出一个以秒为单位的时间。将最大时间设为 0 则是命令浏览器删除该 Cookie。当删除 Cookie 的时候,path 必须保持一直。
4、编程实例:记录用户浏览过的商品信息。
步骤:
(1) 在一个首页中有两个区域,一个区域是显示商品的所有信息,并链接到商品详细信息的页面,另一个区域是显示用户浏览过的商品
(2) 当用户点击某个商品的时候,浏览器向服务器请求访问详细信息页面,并将商品的 id 号传过去。
(3) 详细信息页面通过 id 号,列出商品的详细信息。并构建一个 Cookie 传给客户机保存。response.addCookie 方法会覆盖掉以前的 Cookie ,但是 Cookie 的名字和路径必须要一致。
(4) 当返回到商列表页面时,服务器拿到了用户带过去的 Cookie 进而显示浏览记录。
商品列表页面:
- public class BookIndex extends HttpServlet {
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- response.setContentType("text/html;charset=utf-8");
- PrintWriter out = response.getWriter();
- out.write("本网站有以下商品:<br/><br/>");
- // 输出所有商品信息
- printAll(out);
- out.write("<br/>曾经访问:<br/><br/>");
- // 输出曾经浏览过的最近三件商品
- Cookie[] cookies = request.getCookies();
- for (int i = 0; cookies != null && i < cookies.length; i++) {
- if (cookies[i].getName().equals("hasshow")) {
- printLastVisited(cookies[i].getValue(), out);
- }
- }
- }
- private void printAll(PrintWriter out) {
- Map<String, Book> books = DB.getAll();
- for (Map.Entry<String, Book> entry : books.entrySet()) {
- Book book = entry.getValue();
- out.write("<a href='/day06/bookdetial?id=" + book.getId() + "'>" + book.getName() + "</a><br>");
- }
- }
- private void printLastVisited(String cookie, PrintWriter out) {
- String[] record = cookie.split("\\_");
- Map<String, Book> books = DB.getAll();
- for (int i = 0; i < record.length; i++) {
- out.write(books.get(record[i]).getName() + "<br/>");
- }
- }
- }
详细信息页面:
- <span style="font-family:SimSun;font-size:13px;">public void doGet(HttpServletRequest request, HttpServletResponse response)
- <span style="font-family:Microsoft YaHei;"> throws ServletException, IOException {
- response.setContentType("text/html;charset=utf-8");
- PrintWriter out = response.getWriter();
- String id = request.getParameter("id");
- Map<String, Book> books = DB.getAll();
- // 显示详细信息
- out.write("书名:" + books.get(id).getName() + "作者:"
- + books.get(id).getAuthor());
- boolean has = false;
- // 将访问商品记录写入到客户浏览器
- Cookie[] cookies = request.getCookies();
- for (int i = 0; cookies != null && i < cookies.length; i++) {
- if (cookies[i].getName().equals("hasshow")) {
- String record = writeToClient(cookies[i].getValue(), id);
- Cookie cookie = new Cookie("hasshow", record);
- cookie.setMaxAge(7*24*3600);
- response.addCookie(cookie);
- has = true;
- break;
- }
- }
- if(!has){
- response.addCookie(new Cookie("hasshow", id));
- }
- out.write("<a href='/day06/bookindex'>返回</a>");
- }
- private String writeToClient(String cookie, String id) {
- LinkedList list = new LinkedList( Arrays.asList(cookie.split("\\_")));
- StringBuffer sb = new StringBuffer();
- // 记录不足3条,且曾经没有访问过,将记录加入到第一位
- if (list.size() < 3 && !list.contains(id)) {
- list.addFirst(id);
- }
- // 记录不足3条,且曾经访问过,删除原来,再将记录放入在第一位
- if (list.size() < 3 && list.contains(id)) {
- list.remove(id);
- list.addFirst(id);
- }
- // 记录已经3条,且曾经没有访问过,删除最后一个记录,再将新记录放在第一位
- if (list.size() >= 3 && !list.contains(id)) {
- list.removeLast();
- list.addFirst(id);
- }
- // 记录已经3条,且曾经访问过,删除原来记录,再将记录放在第一位
- if (list.size() >= 3 && list.contains(id)) {
- list.remove(id);
- list.addFirst(id);
- }
- for (Object object : list) {
- String bookid = (String) object;
- sb.append(bookid + "_");
- }
- sb.deleteCharAt(sb.length() - 1);
- return sb.toString();
- }
- }</span></span>
四、Session
在 Web 开发中,服务器可以为每个用户浏览器创建一个独占的会话对象(默认情况下),因此在需要保存用户数据时,服务器程序可以把用户数据写到 session 中,当用户访问其他程序时,可以从用户的 session 对象中取出用户数据。
1、两个 Servlet 程序进行数据传递:通过 request.getSession() 获得一个 Session 对象然后通过 session.setAttribuate("","") 来设置一个属性,当访问第二个 Servlet 程序时,通过 session.getAttribuate("") 来获取数据。注意,当浏览器第一次访问服务器的时候,是没有与之对应的独占 Session 对象的,必须通过 HttpSession session = request.getSession(); 来构建一个。
购买页面:
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- HttpSession session = request.getSession();
- session.setAttribute("product", "洗衣机");
- }
支付页面
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- response.setContentType("text/html;charset=utf-8");
- PrintWriter out = response.getWriter();
- HttpSession session = request.getSession();
- String product = (String) session.getAttribute("product");
- out.write(product);
- }
2、当用户将该浏览器关闭以后,再打开浏览器的时候,上次买的商品信息已经不存在,因为这是两个会话。(只有通过一个浏览器打开的子窗口才有效,无论是弹出新窗口还是选项卡)。服务器会又一次 getSession() 所以两个 Session 对象不一样,所以得不到先前的数据
3、Session 对象的生命周期
用户第一次访问服务器时没有 Session ,当通过 request.getSession(); 以后,服务器才会创建一个 Session。之后 Session 对象会一直驻留在服务器的内存中。当浏览器关闭的时候,服务器并不是马上摧毁 Session 对象,而是默认的将 Session 对象保存 30 分钟。我们可以通过两种方式修改 Session 的摧毁时间:
(1) 修改当前应用的 Web.xml 文件。在 <web-app> 元素下创建子元素(以分钟为单位):
<session-config>
<session-timeout>1</session-config>
</session-config>
(2) 在程序中加入代码:
session.invalidate(); 通过 Session 对象摧毁,该方法会立即摧毁 Session 对象
4、Session 内部机制
Session 的工作其实是基于 Cookie 的。当服务器创建一个 Session 的时候,会为每一个 Session 对象指定一个 ID 号,然后以 Cookie 的形式发送到客户机,让用户再次访问服务器时,会带着有 Session ID 号的 Cookie 提交给服务器。注意,服务器会写给客户机的 Cookie 是一个会话级别的 Cookie,当浏览器关闭以后,Cookie 被清除,所以再次开浏览器的时候,由于没有 Cookie 传递给服务器,所以服务器找不到以前的 Session 对象。
5、更改保存 Session ID 号 Cookie 的生命周期
当用户在购买商品的时候,不小心关闭了浏览器,因为先前的 Cookie 是一个会话级别的,所以 Cookie 会被摧毁。当用户再次打开浏览器的时候,先前买的商品信息就无法获得。因此我们要手动发送 Session ID 号,并更改 Cookie 的有效期。JSESSIONID 为服务器为 Session 创建的 Session ID 号。
在购买页面写入:
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- HttpSession session = request.getSession();
- session.setAttribute("product", "洗衣机");
- String id = request.getSession().getId();
- Cookie cookie = new Cookie("JSESSIONID", id);
- cookie.setMaxAge(30*60*60);
- cookie.setPath("/day06");
- response.addCookie(cookie);
6、当用户禁用 Cookie 的处理情况
由于当用户禁用了 Cookie,所以服务器给客户机发送的 Session ID 号,客户机是收不到的。所以服务器就无法用原来的 Session 为客户机服务。(当用户的主机名为 localhost 访问自己主机的时候,本机是不阻止接受 Cookie 的。)
解决方法:URL 重写。既然用户阻止了 Cookie 接受数据,能么我们可以通过将数据加在 URL 地址栏的后面,作为参数传给客户机。然后客户机再访问浏览器的时候服务器依然可以获取到数据。
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- response.setContentType("text/html;charset=utf-8");
- PrintWriter out = response.getWriter();
- String id = request.getSession().getId();
- request.getSession(); //由于第一次访问没有 session 所以就没有session 号
- String url1 = response.encodeUrl("/day06/buy");
- String url2 = response.encodeUrl("/day06/pay'");
- out.write("<a href='" + url1 + "'>购买</a> ");
- out.write("<a href='" + url2 + "'>结账</a>");
- }
细节:如果当用户没有禁用 Cookie 的时候,首次访问服务器,服务器发现客户机带了 Cookie 过来,所以服务器第二次就自动不重写 URL。如果用户即禁用了 Cookie 又在访问的过程中关闭了浏览器,那么就没有办法再次或得到服务器原来的 session。为了良好的解决为题,我们一般既要通过 Cookie 的方式保存 Session ID ,又要用 URL 重写的方式保存 Session ID。
7、编程实例:编写一个购物车
原理分析:当用户访问商品信息页面时,获取所有商品信息的集合,然后给予显示,并且在每个商品的连接上加入商品的编号 id 以便支付页面使用。当点击购买的时候,商品 id 作为 URL 的参数传递给了支付页面,支付页面根据商品 id 将当前购买的商品加入到购物车当中。购物车存放在 session 中,然后将数据重定向到购物车显示页面。购物车显示页面通过 session 获取已购买商品集合然后显示。
要点:首先支付页面不能用请求转发将数据传递给购物车页面,因为在当页面转发到购物车页面时,如果用户点击了刷新,刷新的功能就是 ( 1)不管浏览器有没有缓存数据,它必像服务器发送请求。(2) 将上次做的事情再做一遍。也就是说如果用了请求转发,那么当用户点击了刷新,就等于又执行了购买操作。所以应该用重定向。当用户禁用了 Cookie 的时候,应用 URL 重写,如果重写了 URL 那么 session 的创建应该在商品信息页面。因为如果用了重定向,浏览器会发两次请求给服务器,那么就无法获取到之前的 session。
商品列表页面:
- public class ShoppingIndex extends HttpServlet {
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- // 只用于显示所有商品信息
- response.setContentType("text/html;charset=utf-8");
- PrintWriter out = response.getWriter();
- out.println("商品列表:<br/><br/>");
- Map<String, Book> books = DB.getAll();
- request.getSession();
- for (Map.Entry<String, Book> entry : books.entrySet()) {
- Book book = entry.getValue();
- String url = response.encodeURL("/day06/shoppingpay?id=" + book.getId());
- out.println("书:" + book.getName() + "作者:" + book.getAuthor()
- + " <a href='"+url+"' target='_blank' >购买</a><br/>");
- }
- }
- }
支付页面:
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- //获取当前购买书的id
- String bookid = request.getParameter("id");
- //获取session用于存放购物车
- HttpSession session = request.getSession();
- //创建存放sessionid的cookie
- String sessionid = session.getId();
- Cookie cookie = new Cookie("JSESSIONID",sessionid);
- cookie.setMaxAge(30*60);
- //cookie.setPath("/day06");
- response.addCookie(cookie);
- //url重写
- String url = response.encodeRedirectURL("/day06/cary");
- //将当前购买的书放入购物车
- List list = (List) session.getAttribute("cary");
- if(list == null)
- list = new ArrayList();
- list.add(bookid);
- //将购物车放入session
- session.setAttribute("cary", list);
- //跳转到购物车
- response.sendRedirect(url);
- }
购物车页面:
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- response.setContentType("text/html;charset=utf-8");
- PrintWriter out = response.getWriter();
- HttpSession session = request.getSession();
- List list = (List) session.getAttribute("cary");
- out.println("已购买:</br></br>");
- Map<String, Book> books = DB.getAll();
- for (int i = 0; i < list.size(); i++) {
- Book book = books.get(list.get(i));
- out.println(book.getName() + "<br/>");
- }
- }
五、防止重复提交
重复提交有三种可能性,(1) 由于网络延迟的可能性,用户以为自己没有提交成功,则多次点击提交按钮,造成服务器负担。(2) 用户提交后,再刷新页面(把上次做的事情再做一次),造成重复提交。(3) 用户注册完以后,后退再点注册。为了减轻服务器压力,有两种方式可以避免重复提交。
1、客户端加上 JavaScript 代码。这种方式不能彻底的阻止用户重复提交,因为 js 是在客户端运行的,用户可以获取源文件,然后删掉 js 代码,自己写一个表单提交给服务器。但是我们还应该加上 js 验证,这样可以有良好的用户体验性。js 防止客户提交很简单,我只说下思路,在表单的 onsubmit 事件中,加入一个验证方法 return check() 。定义一个全局变量,如果用户提交了一次后,更改全局变量的值,下次再提交就可以阻止表单重复提交。
2、在服务器端增加表单验证程序。当用户访问注册页面时,我们在服务器端生成一个令牌,将令牌作为隐藏域一起发给用户。当用户点击注册的时候,连通令牌一起发给服务器,与服务器之前的令牌比对,如果能比对上,则证明表单没有被提交过,提交完以后立即将服务器端存放的令牌清除掉,这样下次再有同样的表单提交,服务器就没有令牌来做比对,这样就可以防止重复提交。
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- response.setContentType("text/html;charset=utf-8");
- PrintWriter out = response.getWriter();
- String card = UUID.randomUUID().toString();
- out.println("<form action='/day06/validate' method='get'>");
- out.println("<input type='text' name='name'/>");
- out.println("<input type='submit' value='提交'/>");
- out.println("<input type='hidden' value='"+card+"' name='card'/>");
- out.println("</form>");
- HttpSession session = request.getSession();
- session.setAttribute("card", card);
- }
- public class Validate extends HttpServlet {
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- String card = (String) request.getSession().getAttribute("card");
- String ccard = request.getParameter("card");
- if(card != null && card.equals(ccard)){
- System.out.println("成功");
- request.getSession().removeAttribute("card");
- }
- else
- System.out.println("重复提交");
- }
- }
六、一次性验证码
显示页面:
- <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
- <%
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <base href="<%=basePath%>">
- <title>My JSP 'img.jsp' starting page</title>
- </head>
- <script type="text/javascript">
- function reloadimg(image){
- image.src = image.src + "?"+ new Date().getTime();
- }
- </script>
- <body>
- <form action="/day06/validateImg" method="post">
- <input type="text" name="img"/>
- <img alt="验证码" src="/day06/createImg" οnclick="reloadimg(this)" style="cursor: hand;" />
- <input type="submit" value="提交"/>
- </form>
- </body>
- </html>
- 图片生成:
- package imgValidate;
- import java.awt.Color;
- import java.awt.Font;
- import java.awt.Graphics;
- import java.awt.image.BufferedImage;
- import java.io.IOException;
- import java.io.PrintWriter;
- 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;
- import javax.servlet.http.HttpSession;
- public class CreateImg extends HttpServlet {
- private static final int WIDTH = 120;
- private static final int HEIGHT = 30;
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- // 在内存中生成一个图片
- BufferedImage image = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB);
- // 构建一个画板
- Graphics g = image.getGraphics();
- // 在画板上填充背景色
- fillBgColor(g);
- // 画矩形边框
- drawRect(g);
- // 画5条干扰线
- drawLine(g);
- // 画4个汉字
- drawWord(g, request.getSession());
- // 构建图片输出流
- response.setContentType("image/gif");
- response.setDateHeader("Expries", -1);
- response.setHeader("Pragma", "no-cache");
- response.setHeader("Cache-Control", "no-cache");
- ImageIO.write(image, "gif", response.getOutputStream());
- }
- private void drawWord(Graphics g, HttpSession session) {
- g.setColor(Color.BLACK);
- g.setFont(new Font("隶书", Font.BOLD, 15));
- String words = "\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";
- Random random = new Random();
- int x = 8;
- StringBuffer sb = new StringBuffer();
- for (int i = 0; i < 4; i++) {
- int n = random.nextInt(words.length());
- sb.append(words.charAt(n));
- g.drawString(words.charAt(n)+"", x, 20);
- x += 30;
- }
- session.setAttribute("img", sb.toString());
- }
- private void drawLine(Graphics g) {
- g.setColor(Color.RED);
- int x = 0;
- int y = 0;
- int x_ = 0;
- int y_ = 0;
- Random random = new Random();
- for (int i = 0; i < 5; i++) {
- x = random.nextInt(WIDTH);
- y = random.nextInt(HEIGHT);
- x_ = random.nextInt(WIDTH);
- y_ = random.nextInt(HEIGHT);
- g.drawLine(x, y, x_, y_);
- }
- }
- private void drawRect(Graphics g) {
- g.setColor(Color.BLUE);
- g.drawRect(1, 1, WIDTH - 2, HEIGHT - 2);
- }
- private void fillBgColor(Graphics g) {
- g.setColor(Color.GREEN);
- g.fillRect(0, 0, WIDTH, HEIGHT);
- }
- }
验证:
- package imgValidate;
- import java.io.IOException;
- import java.io.PrintWriter;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- public class ValidateImg extends HttpServlet {
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- request.setCharacterEncoding("utf-8");
- String validate = request.getParameter("img");
- String svalidate = (String) request.getSession().getAttribute("img");
- System.out.println(validate);
- System.out.println(svalidate);
- if (validate.equals(svalidate)) {
- System.out.println("验证成功");
- } else {
- System.out.println("验证失败");
- }
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doGet(request, response);
- }
- }
注意:1、要想客户端不缓存,应该在响应头中加入:
response.setContentType("image/gif"); // 告诉浏览器我写过去的是张图片
response.setDateHeader("Expries", -1);
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
2、要想点击图片就换一张,那么就要每次请求的地址不一样,要不然浏览器不会提交
image.src = image.src + "?"+ new Date().getTime();