Servlet——Cookie 和 Session

Servlet基础知识Cookie和Session cookie用来在浏览器端存储用户的状态信息,然后再访问后端的时候将这部分信息带回到后端。cookie的主要内容包括:名字、值、过期时间、路径和域。Session是用来在服务器端保存用户的信息。比如购物车,在用户点击“下单”按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建特定的session,用于标识这个用户,并且跟踪用户,这样才知道购物车里面有什么。这个session是保存在服务端的,有一个唯一标识。 阅读详情

 一、会话

 

        会话可简单的理解为:用户的打开一个浏览器,点击多个超链接访问 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/>");
		}
	}
}


详细信息页面:

public void doGet(HttpServletRequest request, HttpServletResponse response)
			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();
	}
}

 

 

四、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" onclick="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();



                      


Servlet第七篇【Cookie和Session的区别、应用】 Session和Cookie的区别 从存储方式上比较Cookie只能存储字符串,如果要存储非ASCII字符串还要对其编码。 Session可以存储任何类型的数据,可以把Session看成是一个容器 从隐私安全上比较Cookie存储在浏览器中,对客户端是可见的。信息容易泄露出去。如果使用Cookie,最好将Cookie加密 Session存储在服务器上,对客户端是透明的。不存在敏感信息泄露问题。 从有 阅读详情

相关推荐

推荐算法之Thompson(汤普森)采样

如果想理解汤普森采样算法,就必须先熟悉了解贝塔分布。 一、Beta(贝塔)分布 Beta分布是一个定义在[0,1]区间上的连续概率分布族,它有两个正值参数,称为形状参数,一般用α和β表示,Beta分布的概率密度函数形式如下: 这里的Γ表示gamma函数。 Beta分布的均值是: 方差: ...

weixin_30508241的博客 4259

Spring Boot2 常用应用程序属性

可以在application.properties文件内部application.yml,文件内部或命令行开关中指定各种属性。本附录提供了常用Spring Boot属性的列表以及对使用它们的基础类的引用。 [注意] 属性贡献可以来自类路径上的其他jar文件,因此您不应将此视为详尽的列表。此外,您可以定义自己的属性。 [警告] 此示例文件仅供参考。千万不能复制和粘贴的全部内容到应用程序中。相反,...

q_0718的博客 1458

电力系统设计实战:如何用ETAP绘制一次接线图并优化供电方案

本文详细介绍了如何使用ETAP软件进行电力系统一次接线图的绘制与供电方案优化。从基础环境配置到主接线拓扑构建,再到典型接线方式的对比分析与智能优化,为工业与民用电力系统设计提供实用指南。通过ETAP的可靠性分析和动态仿真功能,帮助工程师选择最优供电方案,提升系统可靠性和运维效率。

weixin_29250573的博客 205

Spring Boot使用Servlet Session和Spring Session

直接使用Tomcat Session可以满足用户访问量不大的情况,

诗人不写诗 4689

用Multisim仿真AD630锁定放大器:从2012年电赛A题实战到避坑指南

本文详细介绍了如何使用Multisim仿真AD630锁定放大器,从2012年电赛A题实战出发,提供完整的电路搭建、参数优化及避坑指南。重点解析AD630的开关调制架构和双相位检测的工程实现,帮助读者在强噪声背景下有效提取微弱信号,提升电子设计竞赛和工程实践中的信号处理能力。

weixin_42549451的博客 203

玩转 Milvus(二):在 Ubuntu 22.04(WSL2)上安装 Milvus

本文详细介绍了在Ubuntu 22.04(WSL2)环境下安装Milvus向量数据库的两种单机模式:Milvus Lite和Milvus Standalone。Milvus Lite作为轻量级Python库,适合快速学习和小规模实验,只需通过pip安装即可使用。Milvus Standalone则支持Docker和Docker Compose两种部署方式,适合开发测试或小规模生产环境。文章对比了不同安装方式的优缺点,并提供了验证案例和常见问题解答,帮助开发者快速上手,实现简单的语义搜索功能。

企业级后端与 AI 应用开发双线探索 2507

servlet-Cookie与Session

Cookie Cookie是服务器通知客户端保存键值对儿的一种技术 客户端有了Cookie后,每次请求都发送给服务器 每个 Cookie的大小都不超过4kb 注意 Cookie值不包含空格,方括号,圆括号,等号,逗号,双引号,斜杠,问号,at符号,冒号和分号,空值在所有浏览器上的行为也不一样。需要使用BASE64编码。 Cookie生命控制 setMaxAge() 正数,表示在指定的秒数后过期 负数,表示浏览器一关闭,Cookie就会被删除 零 ,表示马上删除 CookiePath属性 Cooki

m0_46656833的博客 366

VMware ESXi +DELL虚拟化存储磁盘容量处于紧急状态问题的处理

1、环境介绍 exsi server两台(DELL R730 ): 192.168.3.206 VMware EXSi6.5 192.168.3.207 VMware EXSi6.5 磁盘存储柜一台(DELL SC2000): 192.168.3.208 2、故障处理 由于前期规划的卷空间过大,虚拟机文件逐年增大以后,造成卷的磁盘占用过与整个磁盘空间的容量,虚拟化平台可用容量爆掉,DELL SC2000自动切换到紧急状态,禁止所有磁盘I/O,exsi主机上的虚拟机无法开机 首先,安装

wolf1105的博客 3533

servlet之session&cookie

会话技术 可以简单理解为:浏览器打开到关闭就是一次会话 session和cookie可以实现会话跟踪技术,功能有重叠一般结合使用 session和cookie结合使用的例子:用户访问浏览器,服务器生成JESSIONID,JESSIONID是一个cookie,每次http请求会携带此cookie,用来让服务器知道选择哪个session,即JESSIONID相当于session的id。(因为服务器中存这好多用户的会话session),这个过程由服务器自动完成。 会话技术可以:记住浏览的商品,可以识别用户,可以

chen2016的博客 1160

C# 零基础入门知识点汇总

本文属于C#零基础入门之百识百例系列目录文章。此系列文章已经完结,欢迎大家点击文中文章链接查看详情进行学习。

陈言必行 -- Unity游戏开发领域优质博主 5348

Cloud Connector RFC 访问控制深度实践:为 SAP BTP ABAP environment 打通安全、可控的远程连接

本文是一份面向企业ABAP团队与平台工程团队的Cloud Connector RFC访问控制配置指南。文章详细介绍了虚拟主机/实例号、负载均衡、资源白名单等核心概念,并提供了从零开始的配置流程。主要内容包括:通过Cloud Connector建立安全隧道的价值(零暴露、最小权限)、两种连接类型(无负载均衡/带负载均衡)的配置方法、资源白名单设置技巧,以及两个典型应用场景(Custom Code Migration远程检查与BAPI调用)的实操案例。指南基于SAP官方文档,包含落地经验与避坑建议,可帮助团队快

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 146

SpringBoot配置属性之Server

SpringBoot配置属性之Server server server.address 指定server绑定的地址 server.compression.enabled 是否开启压缩,默认为false. server.compression.excluded-user-agents 指定不压缩的user-agent,多个以逗号分隔,默认值为:text/html,text/xml,text/pl...

折剑听雨落 5769

74.9008深度刷机原理|高通QPST/Firehose底层刷写协议详解

移动设备刷机与底层维修技术指南 本文为移动设备维修人员和高级玩家提供全面的刷机与底层维修技术指南,涵盖高通、联发科和苹果A系列芯片的主要操作。 主要内容 核心操作:Bootloader解锁、Fastboot刷写、EDL深度修复、iTunes DFU模式恢复和分区表重建 适用场景:系统故障修复、底层损坏恢复、安全限制解除、系统降级和深度硬件维修 关键技术:详细解析刷机协议栈层级、关键分区作用和刷机安全机制 操作步骤: 环境搭建与驱动安装 Bootloader解锁(以小米为例) 高通机型系统刷写 苹果设备DFU

2601_95651806的博客 205

Servlet的Cookie和Session机制

Servlet Cookies Cookies定义:Cookies是存储在客户端计算机上的文本文件,并保留了用户的各种跟踪信息。 Cookies作用:会话保持,如完成用户的登录与状态保持 Cookies的工作原理: 客户端向服务区发起登录请求 服务器脚本(代码)向浏览器发送一组Cookies,例如:姓名,年龄等 浏览器将这些信息存储在本地计算机上,以备将来使用 当下一次浏览器向web服务器发送任何请求时。浏览器会把这些Cookies信息发送到服务器,服务器将使用这些信息来识别账户 Cookies构成 C

wkh18891843165的博客 1769

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Servlet(Cookie和Session)

🐲 1. Cookie 的工作流程 🐲 2. Servlet中操作 Cookie 和 Session 的api 🐲 3. 案例1: 模拟登录 🐲 4. 上传文件 🐲 5. 案例2: 上传文件

m0_58761900的博客 1928

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Servlet、JSP入门、Cookie和Session总结篇

Servlet、JSP入门、Cookie和Session总结篇

m0_72900498的博客 1100

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

servlet中cookie和session操作

1.1 软件中的会话 一次会话: 打开浏览器 -> 访问一些服务器内容 -> 关闭浏览器   登录场景: 打开浏览器 -> 浏览到登陆页面 -> 输入用户名和密码 -> 访问到用户主页(显示用户名) 修改密码(输入原密码)  修改收货地址 .......   问题:在此处登录会话过程中产生的数据(用户会话数据)如何保存下来呢?   购物场景:  打开浏览器 -> 浏览商品列表  -> 加入购物车(

oschina_39664569的博客 1万+

Servlet——Servlet中Cookie和Session

Session import javax.servlet.ServletException; 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 .

char_m的博客 412

Servlet会话:Cookie和Session

会话技术 在了解cookie和session之前,需要先了解一个概念:会话 1. 概念 在日常生活中,从拨通电话到挂断电话之间的一连串的你问我答的过程就是一个会话。web应用中的会话过程类似于生活中的打电话过程,它指的是客户端(浏览器)与web服务器之间连续发生的一系列请求和响应的过程。例如:一个用户在某个网站上的整个购物过程就是一个会话。 2. HttpServletRequest对象和ServletContext HttpServletRequest对象和ServletContext都可以

野生码农 512

Servlet【 ServletAPI中的会话管理Cookie与Session】

服务器同一时刻收到的请求是很多的. 服务器需要清除的区分清楚每个请求是从属于哪个用户, 就需要在服务器这边记录每个用户令牌以及用户的信息的对应关系.在上面的例子中, 就诊卡就是一张 “令牌”. 要想让这个令牌能够生效, 就需要医院这边通过系统记录每个就诊卡和患者信息之间的关联关系.默认情况下 HTTP 协议的客户端和服务器之间的这次通信, 和下次通信之间没有直接的联系.但是实际开发中, 我们很多时候是需要知道请求之间的关联关系的.●Cookie 是客户端的机制. Session 是服务器端的机制.

积跬步以至千里 1694

servlet中cookie和session源码浅析

servlet中cookie和session源码浅析 文章目录servlet中cookie和session源码浅析一、简介二、cookie2.1 cookie特点2.2 cookie源码2.3 cookie示例三、session3.1 session特点3.2 HttpSession源码3.3 session示例 一、简介 http协议是无状态的,在web会话中,为了记录会话状态信息,将使用coo...

panda-star的博客 455

Servlet对Cookie、Session的管理

Cookie、Session、cache的区别之前已经做过解释,不再赘述(http://blog.csdn.net/lmdcszh/article/details/8986312)。这里重点总结一下Servlet对Cookie、Session的代码操作实现,作为一个基础是需要熟练运用的。 *************************************针对Cookie的操作**

李黎敏 ——为优秀j2ee架构师而努力 3239

Servlet、Session、Cookie

Servlet Servlet 官方API文档 Servlet调用过程 具体调用过程 Servlrt 两个继承类 GenericServlet HttpServlet Servlet 细节 同一个Servlet可以被映射到多个URL上 在Servlet映射到URL中也可以使用通配,但是只能由两种固定格式:.扩展名;/* Servlet 对象创建在浏览器第一次...

qq_40535097的博客 574

Java EE Servlet之Cookie 和 Session

cookie 和 session浏览器首次访问到登录操作的时候,就会在服务器这边验证身份,验证通过,就会创建会话服务器就会保存会话消息(hash)客户端也会保存身份标识(sessionId)后续浏览器在访问这个网站(网站的其他界面)都会带上 cookie(sessionId)服务器不需要让浏览器重新登陆,也能识别出浏览器的用户身份信息。

WR1207的博客 1343

Servlet的Cookie和Session问题分析

1.状态管理 ## (1)什么是状态管理? 将浏览器与web服务器之间多次交互当做一个整体来处理,并且 将多次交互所涉及的数据(即状态)保存下来。 ## (2)如何进行状态管理 1)将状态保存在客户端(浏览器) Cookie 2)将状态保存在服务器端 session ## (3)Cookie 1)什么是Cook...

qq_36142320的博客 501

Servlet——Cookie和Session

首先介绍以下Servlet的请求转发和重定向。请求转发和重定向都是Servlet的跳转技术 请求转发:request.getRequestDispatcher("dispatcher.jsp").forward(request,response) 请求转发:request.getRequestDispatcher("dispatcher.jsp").include(request,respons...

Stay Hungry Stay Foolish 350
上一篇: Servlet——Response 和 Resquest
下一篇: Jsp 和 EL 表达式
DriverKing
博客等级 码龄16年 77粉丝 54原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值