欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Servlet中不可不知的Session技术

程序员文章站 2024-03-16 22:08:28
...

目录

介绍

  Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的Session对象,由于Session为用户浏览器独享,所以用户在访问服务器的Web资源时,可以把各自的数据放在各自的Session中,当用户再去访问服务器中的其它Web资源时,其它Web资源再从用户各自的Session中取出数据为用户服务。
  在Web开发中,服务器可以为每个用户浏览器创建一个会话对象(Session对象),注意:一个浏览器独占一个Session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。

Session和Cookie的主要区别

  • Cookie是把用户的数据写给用户的浏览器;
  • Session技术把用户的数据写到用户独占的session中(即保存在服务器端)。

Session的创建

  在一次会话中,服务器首次需要针对该会话操作session时(在Java Web中即request.getSession(),在PHP中即session_start()),服务器才创建session,而不是用户首次访问服务器的某个网站后就立刻创建session的。
  参考:

session什么时候被创建 http://blog.sina.com.cn/s/blog_89a6f5b001010gy9.html
session的创建和时间设置 http://blog.csdn.net/w8998036/article/details/51026966
php创建session的方法实例详解 http://www.jb51.net/article/60397.htm

  Session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。如:

HttpSession session = request.getSession();

  使用过程中,没有向getSession方法传递参数,则表示session未创建的情况下先创建session后再返回session对象,如果session已被创建则直接返回session对象。getSession方法存在对象的重载方法,接收一个布尔类型的参数。如:

HttpSession session = request.getSession(false);

  getSession(false) 则表示不管session有没有被创建,都不再创建session;getSession(true) 则强制创建session。

Session的销毁

  在Java中,默认session在30分钟之内未被使用,服务器就会将session销毁,不管用户浏览器此时有没有关闭。即便用户关闭了浏览器,结束了当前会话,session也不会立即销毁,直到默认时效内session未被使用。
  可以通过Web应用的配置文件web.xml,配置session的有效时间,如这里设置为10分钟:

<session-config>
    <session-timeout>10</session-timeout>
</session-config>

  手动销毁session,则调用session对象的方法,如:

session.invalidate();

Session的实现原理

  用户打开浏览器访问服务器的某一应用资源后,如 ServletDemo(用户向服务器的一个Servlet发起了请求)。在该Servlet中服务器调用:

HttpSession session = request.getSession();

  则服务器将会判断针对当次会话是否创建了Session,实际上在创建Session的过程中即服务器为该会话生成了一个唯一的标识,我们称之为Session ID,与此同时,请求响应将返回保存了Session ID的Cookie信息。
  该Cookie的名称为JSESSIONID(在PHP中则为PHPSESSID),默认当浏览器关闭时该Cookie将被销毁。
  浏览器端通过获取到该Cookie信息后,用户下次再发起请求时(可以请求与上一次不同的Web资源),则将携带Cookie信息到服务器,服务器通过该Cookie就可以根据Session ID得知当前会话保存的Session。上次请求操作保存在session中的信息,在下次请求操作也同样可以读取到session中保存的信息。

解决浏览器关闭后就立即丢失Session ID的问题

  根据Session实现原理,我们可以知道,在默认情况下,当我们关闭浏览器后保存Session ID的Cookie也将被销毁,即我们再次打开浏览器发送相同请求,获得到的Session对象也不再与之前相同。
  需要注意的是,关闭浏览器后服务器存储的Session不一定就被销毁了,服务器存储的Session是在一定时间内未被操作,服务器才会将其销毁。这里关闭浏览器之后无法再使用上次Session的原因,在于我们丢失了Session ID(保存Cookie的Session ID默认在浏览器关闭时销毁)。
  那么解决这样的问题,我们只需要延长保存Session ID的Cookie的有效期限即可。如:

//=========== 解决浏览器关闭后就立即丢失Session ID的问题(延长保存Session ID的Cookie的有效期,默认没有设置该Cookie的有效期,即浏览器关闭则该Cookie被销毁) ============
String sessionId = session.getId();
Cookie cookie = new Cookie("JSESSIONID", sessionId);
cookie.setPath("/day07/"); // path的值需要与原来的一致才行
cookie.setMaxAge(30 * 60); // 设置为30分钟内有效
response.addCookie(cookie);
//=========== 解决浏览器关闭后就立即丢失Session ID的问题 ============

浏览器禁用Cookie后如何使用Session技术?

  从Session的实现原理中,我们可以发现Session的实现需要依赖于Cookie,那么当用户浏览器禁用了Cookie后又该如何使用Session技术呢?
  Session的实现原理最基本的是要求用户在每次请求时发送保存有Session ID的信息(如发送保存有Session ID的Cookie)。禁用Cookie后,我们可以为页面中每条请求增加一个参数,用于发送Session ID的值。这样,服务器获取到Session ID后就可以获取到用户独占的Session,为用户服务。
  在Java中,通过request对象的getSession方法获取session对象,getSession方法会先判断请求是否发送了含有Session ID的Cookie过来,如果没有,则会判断请求中是否发送了保存Session ID的参数过来。这两种情况下都未能获取到Session ID的话,服务器则判定还未替当前用户创建独占的session,并为用户进行创建。
  示例:
WelcomeServlet.java

package com.wm103.session;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Created by DreamBoy on 2017/5/2.
 */
@WebServlet(name = "WelcomeServlet", urlPatterns = {"/WelcomeServlet"})
public class WelcomeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.getSession();

        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        String url1 = response.encodeURL("/day07/SessionDemo1"); // 通过encodeURL方法为该请求增加Session ID的参数;如前面getSession方法是通过Cookie获取到Session ID的,则这里不会增加Session ID这个参数,因为服务器可以判断用户浏览器未禁用Cookie。如果是首次创建Session同样也会添加Session ID参数。
        String url2 = response.encodeURL("/day07/SessionDemo2");
        out.print("<a href='" + url1 + "'>购买</a>   ");
        out.print("<a href='" + url2 + "'>结账</a>");
    }
}

  注:通过repsonse对象的encodeURL方法为该请求增加Session ID的参数;如前面getSession方法是通过Cookie获取到Session ID的,则这里不会增加Session ID这个参数,因为服务器可以判断用户浏览器未禁用Cookie。如果是首次创建Session同样也会添加Session ID参数。
SessionDemo1.java

package com.wm103.session;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;

/**
 * Created by DreamBoy on 2017/5/1.
 */
@WebServlet(name = "SessionDemo1", urlPatterns = {"/SessionDemo1"})
public class SessionDemo1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        session.setAttribute("name", "笔记本");
    }
}

ServletDemo2.java

package com.wm103.session;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * Created by DreamBoy on 2017/5/1.
 */
@WebServlet(name = "SessionDemo2", urlPatterns = {"/SessionDemo2"})
public class SessionDemo2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        HttpSession session = request.getSession();
        response.getWriter().write((String) session.getAttribute("name"));
    }
}

  示例效果(这里演示的是未禁用Cookie的情况下的使用效果):

  • 用户首次访问时,服务器创建Session,此时页面中所有请求的链接均被加上Session ID参数值。

Servlet中不可不知的Session技术

  • 用户点击购买后,返回首页,刷新。此时服务器将接收到浏览器端的Cookie,判定用户浏览器未禁用Cookie,则未向页面中的所有请求链接添加Session ID参数值。

Servlet中不可不知的Session技术

  • 用户点击结账,查看购买的商品

Servlet中不可不知的Session技术

Session案例

用session实现简单的购物车效果

  将购买后的商品保存在session中,显示购物车页面时,从session中取出所有购买后的商品。
  显示商品列表 ListBookServlet.java

package com.wm103.shopping;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Created by DreamBoy on 2017/5/2.
 */
@WebServlet(name = "ListBookServlet", urlPatterns = {"/ListBookServlet"})
public class ListBookServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        // 输出网站所有商品
        out.print("<div style='margin: 20px 50px; padding: 10px 30px; border: 1px solid #eee; box-shadow: 0 2px 2px #ede'>");
        out.write("<h3>本网站有如下商品:</h3>");
        out.print("<ul>");
        Map<String, Book> map = Db.getAll();
        for (Map.Entry<String, Book> entry: map.entrySet()) {
            Book book = entry.getValue();
            out.print("<li style='line-height: 2'>" + book.getName() + " <a href='/day07/BuyServlet?id=" + book.getId() + "' target='_blank'>购买</a></li>");
        }
        out.print("</ul>");
        out.print("</div>");
    }
}

class Db {
    private static Map<String, Book> map = new LinkedHashMap<>();
    static { // 静态代码块
        map.put("1", new Book("1", "JavaWeb开发", "007号", "一本关于JavaWeb开发的书"));
        map.put("2", new Book("2", "JDBC开发", "008号", "一本关于JDBC开发的书"));
        map.put("3", new Book("3", "Spring开发", "009号", "一本关于Spring开发的书"));
        map.put("4", new Book("4", "Struts开发", "010号", "一本关于Struts开发的书"));
        map.put("5", new Book("5", "Android开发", "011号", "一本关于Android开发的书"));
    }

    public static Map<String, Book> getAll() {
        return map;
    }
}

class Book implements Serializable {
    private String id;
    private String name;
    private String author;
    private String description;

    public Book() {
    }

    public Book(String id, String name, String author, String description) {
        this.id = id;
        this.name = name;
        this.author = author;
        this.description = description;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

  处理购买请求 BuyServlet.java

package com.wm103.shopping;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by DreamBoy on 2017/5/2.
 */
@WebServlet(name = "BuyServlet", urlPatterns = {"/BuyServlet"})
public class BuyServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String id = request.getParameter("id");
        Book book = Db.getAll().get(id);

        HttpSession session = request.getSession();
        // 手工以Cookie形式发SessionId,以解决关闭浏览器后,重新打开浏览器上次购买的东西还在

        // 从session中得到用户用于保存所有书的集合(购物车)
        List list = (List) session.getAttribute("list");
        if(list == null) {
            list = new ArrayList<>();
        }
        list.add(0, book);
        session.setAttribute("list", list);

        //response.sendRedirect("/day07/ListCartServlet");
        response.sendRedirect(request.getContextPath() + "/ListCartServlet");
    }
}

  显示购物车页面 ListCartServlet.java

package com.wm103.shopping;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

/**
 * Created by DreamBoy on 2017/5/2.
 */

/**
 * 显示用户购买的商品
 */
@WebServlet(name = "ListCartServlet", urlPatterns = {"/ListCartServlet"})
public class ListCartServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        HttpSession session = request.getSession(false);
        if(session == null) {
            out.write("您还没有购买任何商品!" + "<a href='/day07/ListBookServlet'>前往购买</a>");
            return;
        }

        List<Book> list = (List) session.getAttribute("list");
        out.print("<div style='margin: 20px 50px; padding: 10px 30px; border: 1px solid #eee; box-shadow: 0 2px 2px #ede'>");
        out.write("<h3>您购买了如下商品:</h3>");
        out.print("<ul>");
        for (Book book: list) {
            out.print("<li style='line-height: 2'>" + book.getName() + "</li>");
        }
        out.print("</ul>");
        out.print("</div>");
    }
}

利用session完成用户登录功能

  登录页面 login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<form action="/day07/LoginServlet" method="post">
    <label>用户名:</label><input type="text" name="username"><br/>
    <label>密 码:</label><input type="password" name="password"><br/>
    <input type="submit" value="登录">
</form>
</body>
</html>

  处理登录请求 LoginServlet.java

package com.wm103.login;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

/**
 * Created by DreamBoy on 2017/5/2.
 */
@WebServlet(name = "LoginServlet", urlPatterns = {"/LoginServlet"})
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        List<User> list = DB.getAll();
        for(User user: list) {
            if(user.getUsername().equals(username) && user.getPassword().equals(password)) {
                HttpSession session = request.getSession();
                session.setAttribute("user", user); // 登录成功,向session存入一个登录标记
                response.sendRedirect(request.getContextPath() + "/index.jsp");
                return;
            }
        }

        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.write("用户名或密码错误!");
    }
}

  User.java

package com.wm103.login;

/**
 * Created by DreamBoy on 2017/5/2.
 */
public class User {
    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

  模拟数据库操作 DB.java

package com.wm103.login;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by DreamBoy on 2017/5/2.
 */
public class DB {
    public static List<User> list = new ArrayList<>();
    static {
        list.add(new User("aaa", "123"));
        list.add(new User("bbb", "123"));
        list.add(new User("ccc", "123"));
    }
    public static List getAll() {
        return list;
    }
}

  登录成功后跳转首页 index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  欢迎您,${user.username}! <a href="/day07/LogoutServlet">退出登录</a>
  </body>
</html>

  退出登录 LogoutServlet.java

package com.wm103.login;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * Created by DreamBoy on 2017/5/2.
 */

/**
 * 用户注销
 */
@WebServlet(name = "LogoutServlet", urlPatterns = {"/LogoutServlet"})
public class LogoutServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession(false);
        if(session == null) {
            response.sendRedirect(request.getContextPath() + "/index.jsp");
            return;
        }

        session.removeAttribute("user");
        response.sendRedirect(request.getContextPath() + "/login.html");
    }
}

服务器端session防表单重复提交

  • 表单页面由servlet程序生成,servlet为每次产生的表单页面分配一个唯一的随机标识号,并在form表单的一个隐藏字段中设置这个标识号,同时在当前用户的Session域中保存这个标识号。
  • 当用户提交form表单时,负责处理表单提交的servlet得到表单提交的标识号,并与session中存储的标识号比较,如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。
  • 在下列情况下,服务器程序将拒绝用户提交的表单请求:
    • 存储Session域中的表单标识号与表单提交的标识号不同
    • 当前用户的Session中不存在表单标识号
    • 用户提交的表单数据中没有标识号字段

处理生成随机标识号的Servlet FormSerlvet.java

package com.wm103.form;

import sun.misc.BASE64Encoder;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;

/**
 * Created by DreamBoy on 2017/5/2.
 */
@WebServlet(name = "FormServlet", urlPatterns = {"/FormServlet"})
public class FormServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 产生随机数(表单号)
        TokenProcessor tp = TokenProcessor.getInstance();
        String token = tp.generateToken();

        request.getSession().setAttribute("token", token);
        request.getRequestDispatcher("/form.jsp").forward(request, response);
    }
}

class TokenProcessor {
    /*
     * 1. 把构造方法私有
     * 2. 自己创建一个
     * 3. 对外暴露一个方法:允许获取上面创建的对象
     */
    private TokenProcessor() {}
    private static final TokenProcessor instance = new TokenProcessor();

    public static TokenProcessor getInstance() {
        return instance;
    }

    public String generateToken() {
        String token = System.currentTimeMillis() + new Random().nextInt() + "";
        try {
            MessageDigest md = MessageDigest.getInstance("md5"); // 消息摘要
            byte[] md5 = md.digest(token.getBytes()); // 128位,16字节

            // base64编码
            BASE64Encoder encoder = new BASE64Encoder();
            return encoder.encode(md5);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}

  显示form表单的页面 form.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>防止表单重复提交</title>
</head>
<body>
<form action="/day07/DoFormServlet" method="post">
    <input type="hidden" name="token" value="${token}">
    <label>用户名:</label><input type="text" name="username"><br/>
    <input type="submit" value="提交">
</form>
</body>
</html>

  处理表单请求 DoFormServlet.java

package com.wm103.form;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * Created by DreamBoy on 2017/5/2.
 */

/**
 * 处理表单提交请求
 */
@WebServlet(name = "DoFormServlet", urlPatterns = {"/DoFormServlet"})
public class DoFormServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /*String username = request.getParameter("username");

        // 模拟网络延迟
        try {
            Thread.sleep(1000*3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("向数据库中注册用户——" + username);*/

        boolean b = isTokenValid(request);
        if(!b) {
            System.out.println("请不要重复提交表单!");
            return;
        }

        request.getSession().removeAttribute("token");
        String username = request.getParameter("username");
        System.out.println("向数据库中注册用户——" + username);
    }

    /**
     * 判断表单号是否有效
     * @param request
     * @return
     */
    private boolean isTokenValid(HttpServletRequest request) {
        String clientToken = request.getParameter("token");
        if (clientToken == null || clientToken.equals("")) {
            return false;
        }
        HttpSession session = request.getSession(false);
        if (session == null) {
            return false;
        }
        String serverToken = (String) session.getAttribute("token");
        return serverToken != null && clientToken.equals(serverToken);
    }
}

利用session校验图片验证码

  生成验证码图片后,并用session保存验证码的内容用于校验下次用户输入提交的验证码是否正确。
  生成图片验证码 ImageServlet.java

package com.wm103.checkcode;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

/**
 * Created by DreamBoy on 2017/4/28.
 */

/**
 * 输出一张随机图片(图片验证码)
 */
@WebServlet(name = "ImageServlet", urlPatterns = {"/ImageServlet"})
public class ImageServlet extends HttpServlet {
    public static final int WIDTH = 120;
    public static final int HEIGHT = 50;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();

        // 1. 设置背景色
        this.setBackground(g);

        // 2. 设置边框
        this.setBorder(g);

        // 3. 画干扰线
        this.drawRandomLine(g);

        // 4. 写随机数
        String random = this.drawRandomNum((Graphics2D) g);
        request.getSession().setAttribute("checkcode", random);

        // 5. 图片写给浏览器
        response.setContentType("image/jpeg");
        // 控制浏览器不要缓存图片
        response.setDateHeader("expires", -1);
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");

        ImageIO.write(image, "jpg", response.getOutputStream());

    }

    private void setBackground(Graphics g) {
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, WIDTH, HEIGHT);
    }

    private void setBorder(Graphics g) {
        g.setColor(Color.BLUE);
        g.drawRect(1, 1, WIDTH - 2, HEIGHT - 2);
    }

    private void drawRandomLine(Graphics g) {
        g.setColor(Color.GREEN);
        for(int i = 0; i < 5; i++) {
            int x1 = new Random().nextInt(WIDTH);
            int y1 = new Random().nextInt(HEIGHT);

            int x2 = new Random().nextInt(WIDTH);
            int y2 = new Random().nextInt(HEIGHT);
            g.drawLine(x1, y1, x2, y2);
        }
    }

    private String drawRandomNum(Graphics2D g) {
        int fontSize = 20;
        g.setColor(Color.RED);
        g.setFont(new Font("宋体", Font.BOLD, fontSize));

        String base = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789";
        int fontNum = 4;
        int y = 32;
        int x = 18; String ch;
        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < fontNum; i++) {
            int degree = new Random().nextInt() % 30;
            double radian = degree * Math.PI / 180; // 弧度
            g.rotate(radian, x, y); // 设置旋转的弧度

            ch = base.charAt(new Random().nextInt(base.length())) + "";
            sb.append(ch);
            g.drawString(ch, x, y);

            g.rotate(-radian, x, y);
            x += 25;
        }
        return sb.toString();
    }
}

  使用图片验证码页面 register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户注册</title>
</head>
<body>
<form action="/day07/RegisterServlet" method="post">
    <p><label>用户名:</label><input type="text" name="username"></p>
    <p><label>密 码:</label><input type="password" name="password"></p>
    <p><label>验证码:</label><input type="text" name="checkcode">
    <img style="cursor:pointer;" src="/day07/ImageServlet" alt="换一张" onclick="this.src = '/day07/ImageServlet?' + new Date().getTime()"></p>
    <input type="submit" value="注册">
</form>
</body>
</html>

  处理注册请求,这里只校验填写的验证码是否正确 RegisterServlet.java

package com.wm103.checkcode;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Created by DreamBoy on 2017/5/2.
 */
@WebServlet(name = "RegisterServlet", urlPatterns = {"/RegisterServlet"})
public class RegisterServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // request.setCharacterEncoding("UTF-8"); // 如果是中文验证码则需要处理中文编码的问题

        // 处理注册请求之前,校验验证码是否有效
        String c_checkcode = request.getParameter("checkcode");
        String s_checkcode = (String) request.getSession().getAttribute("checkcode");
        System.out.println(c_checkcode);
        System.out.println(s_checkcode);
        if(c_checkcode != null && s_checkcode != null && c_checkcode.toLowerCase().equals(s_checkcode.toLowerCase())) {
            System.out.println("处理注册请求!");
        } else {
            System.out.println("验证码验证失败!");
        }
    }
}