一、会话的概念

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

二、会话过程中要解决的一些问题

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

三、保存会话数据的两种技术

1、Cookie

Cookie意为"甜饼",是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。

由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。

Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。

2、Session

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

四、Cookie类的主要方法

int getMaxAge()返回Cookie过期之前的最大时间,以秒计算。void setMaxAge(intexpiry)以秒计算,设置Cookie过期时间。String getName()返回Cookie的名字。名字和值是我们始终关心的两个部分,笔者会在后面详细介绍 getName/setName。void setValue(String newValue)Cookie创建后设置一个新的值。String getValue()返回Cookie的值。笔者也将在后面详细介绍getValue/setValue。void setDomain(String pattern)设置cookie中Cookie适用的域名String getDomain()返回Cookie中Cookie适用的域名. 使用getDomain() 方法可以指示浏览器把Cookie返回给同 一域内的其他服务器,而通常Cookie只返回给与发送它的服务器名字完全相同的服务器。注意域名必须以点开始(例如.http://yesky.com)void setPath(String uri)指定Cookie适用的路径。String getPath()返回Cookie适用的路径。如果不指定路径,Cookie将返回给当前页面所在目录及其子目录下 的所有页面。void setSecure(boolean flag)指出浏览器使用的安全协议,例如HTTPS或SSL。boolean getSecure()如果浏览器通过安全协议发送cookies将返回true值,如果浏览器使用标准协议则返回false值。void setVersion(int v)Cookie所遵从的协议版本。int getVersion()返回Cookie所遵从的协议版本。void setComment(String purpose)设置cookie中注释。String getComment()返回Cookie中注释,如果没有注释的话将返回空值。Cookie(String name, String value)实例化Cookie对象,传入cooke名称和cookie的值。

response接口也中定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段。 同样,request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。

五、Cookie使用

1、使用cookie记录用户上一次访问的时间

public class CookieDemo extends HttpServlet{private static final long serialVersionUID = 5757885987685925915L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//设置服务器以UTF-8编码输出resp.setCharacterEncoding("UTF-8");//设置浏览器以UTF-8编码进行接收resp.setContentType("text/html;charset=UTF-8");PrintWriter out = resp.getWriter();//获取Cookie数组Cookie[] cookie = req.getCookies();if(cookie == null){out.write("这是你的第一次访问!");} else {for (Cookie ck : cookie) {if(ck.getName().equals("cookieName")){//获取Cookie里面保存的数据Long time = Long.parseLong(ck.getValue());Date date = new Date(time);out.write("上次访问时间:" + date.toLocaleString());}}}//创建一个cookie,cookie的名字是cookieNameCookie cookies = new Cookie("cookieName", System.currentTimeMillis()+"");//设置Cookie的有效期为1天,这样即使关闭了浏览器,下次再访问时,也依然可以通过cookie获取用户上一次访问的时间。cookies.setMaxAge(24*60*60);//将cookie对象添加到response对象中resp.addCookie(cookies);}
}

第一次访问时,如下所示:

再次访问:

2、删除Cookie

public class CookieDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {//创建一个名字为lastAccessTime的cookieCookie cookie = new Cookie("lastAccessTime", System.currentTimeMillis()+"");//将cookie的有效期设置为0,命令浏览器删除该cookiecookie.setMaxAge(0);req.addCookie(cookie);}
}

3、cookie中存/取中文

public class CookieDemo2 extends HttpServlet{private static final long serialVersionUID = 5757885987685925915L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//设置服务器以UTF-8编码输出resp.setCharacterEncoding("UTF-8");//设置浏览器以UTF-8编码进行接收resp.setContentType("text/html;charset=UTF-8");PrintWriter out = resp.getWriter();//创建一个cookie,cookie的名字是cookieName//存储中文时,使用URLEncoder类里面的encode(String s, String enc)方法进行中文转码Cookie cookies = new Cookie("cookieName", URLEncoder.encode("哎哟!不错哟", "UTF-8"));//将cookie对象添加到response对象中resp.addCookie(cookies);//获取Cookie数组Cookie[] cookie = req.getCookies();if(cookie != null){for (Cookie ck : cookie) {if(ck.getName().equals("cookieName")){//获取Cookie里面保存的数据String text = ck.getValue();//使用URLDecoder类里面的decode(String s, String enc)进行解码out.write("存储的中文数据:" + URLDecoder.decode(text, "UTF-8"));}}}}
}

结果如下:

Cookie注意细节

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

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

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

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

六、Session简单介绍

在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。

Session和Cookie的主要区别

1,Cookie是把用户的数据写给用户的浏览器。

2,Session技术把用户的数据写到用户独占的session中。

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

七、Session基础知识

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

当用户打开浏览器,访问某个网站操作session时,服务器就会在服务器的内存为该浏览器分配一个session对象,该session对象被这个浏览器独占。

这个session对象也可以看做是一个容器,session默认存在时间为30min,你可以修改。

1、Session可以用来做什么

1、网上商城中的购物车

2、保存登录用户的信息

3、将某些数据放入到Session中,供同一用户的各个页面使用

4、防止用户非法登录到某个页面。

2、Session基本使用

request.getSession()返回这个request绑定的session对象,如果没有,则创建一个request.getSession(boolean create)返回这个request绑定的session对象,如果没有,则根据create的值决定是否创建一个session.setAttribute(String name,Object val)向session中添加属性session.getAttribute(String name)从session中得到某个属性session.removeAttribute(String name)从session中删除某个属性session.setMaxInactiveInterval()设置Session的生命周期(单位秒),Session的生命周期默认是30minsession.invalidate()清除所有sessionsession.removeAttribute(String name)删除指定名称的session

Servlet1:

public class Servlet1 extends HttpServlet {private static final long serialVersionUID = -8236507185410764108L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取sessionHttpSession session = req.getSession();//将数据存储到session中session.setAttribute("name", "Zender");}
}

Servlet2:

public class Servlet2 extends HttpServlet {private static final long serialVersionUID = -8236507185410764108L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取sessionHttpSession session = req.getSession();//获取nameString name = (String) session.getAttribute("name");PrintWriter out = resp.getWriter();out.print("name:" + name);}
}

同一浏览器访问Servlet1,再访问Servlet2,结果如下:

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; line-height: inherit; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">image</figcaption>

不同浏览器访问Servlet1,再访问Servlet2,结果如下:

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; line-height: inherit; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">image</figcaption>

可以看到这时候name是null,也就是没有从session对象中取出值,因为360浏览器并没有运行Servlet1来创建Session对象,上面的session对象是Chrome浏览器独占的。

3、Session生命周期

Session中的属性的默认生命周期是30min,这个默认时间可以通过修改web.xml文件来修改

1,在Tomcat根目录confweb.xml文件中修改

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

2,如果只需要对某一个web应用设置,则只需要修改对应web应用的web.xml文件。在这个web.xml文件中添加如上的代码:

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

除了设置默认生命周期之外,最重要的是在程序中设置,调用setMaxInacttiveInterval(int interval),这里的interval是以秒为单位的,而且这个方法设置的是发呆时间,比如你设置的是60秒,那么在这60秒之内如果你没有操作过session,它就会自动删除,如果你操作过,不管是设置属性还是读取属性,它都会从头开始计时。

session.setMaxInactiveInterval(60);

八、Session实现原理

服务器是如何实现一个session为一个用户浏览器服务的?

image

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; line-height: inherit; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">image</figcaption>

1,浏览器A先访问Servlet1,这时候它创建了一个Session,ID号为110,然后Servlet1将这个ID号以Cookie的方式返回给浏览器A。

2,浏览器A继续访问Servlet2,那么这个请求会带上Cookie值: JSESSIONID=110,然后服务器根据浏览器A传递过来的ID号找到内存中的这个Session。

3,浏览器B来访问Servlet1了,它的请求并没有带上 JSESSIONID这个Cookie值,由于它也要使用Session,所以服务器会新创建一个Session,ID号为111。

4,浏览器B继续访问Servlet2,那么这个请求会带上Cookie值: JSESSIONID=111,然后服务器根据浏览器B传递过来的ID号找到内存中的这个Session。

例如:

Servlet1:

public class Servlet1 extends HttpServlet {private static final long serialVersionUID = -8236507185410764108L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取sessionHttpSession session = req.getSession();//将数据存储到session中session.setAttribute("name", "Zender");PrintWriter out = resp.getWriter();out.print("create Session OK");}
}

Servlet2:

public class Servlet2 extends HttpServlet {private static final long serialVersionUID = -8236507185410764108L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取sessionHttpSession session = req.getSession();//获取nameString name = (String) session.getAttribute("name");PrintWriter out = resp.getWriter();out.println("Session ID:" + session.getId());out.print("name:" + name);}
}

第一次访问Servlet1时,服务器会创建一个新的sesion,并且把session的Id以cookie的形式发送给客户端浏览器,如下图所示:

可以看到,Request Headers中并没有Cookie的信息,而Response Headers中有这么一句话:

Set-Cookie:JSESSIONID=05A94199DDC64311563740CC2C78D656; Path=/CookieAndSession/; HttpOnly

说明这个时候服务器向客户端通过Cookie传递回了 JSESSIONID这个属性。

然后访问Servlet2,如下图所示:

可以看到Response Headers中没有出现Set-Cookie这个头,而Request Headers中带上了Cookie这个头:

Cookie:JSESSIONID=05A94199DDC64311563740CC2C78D656

而这个头中正包含 JSESSIONID,并且它的值也就是我们之前Set-Cookie中 JSESSIONID的值。

这就证明了我们之前图解的Session的原理,也就是服务器能够为不同的浏览器区分不同的Session的机制。

九、Session的简单应用

1,用户登录时候验证验证码

Index.jsp:

<head>
<base href="<%=basePath%>" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>表单提交</title>
<link href="css/bootstrap.css" rel="stylesheet">
<script src="js/jquery-3.2.1.js"></script>
<script src="js/bootstrap.js"></script>
</head>
<body><form class="form-horizontal" action="<%=request.getContextPath()%>/CodeServlet.html" role="form" method="post"><div class="form-group"><label for="firstname" class="col-sm-1 control-label">验证码</label><div class="col-sm-3"><input type="text" class="form-control" name="idCodeNum"placeholder="请输入验证码"></div><img src="idCode" onclick="this.src+=''" style="cursor:pointer;" width="115" height="30" title="看不清?换一个"></div><div class="form-group"><div class="col-sm-offset-1 col-sm-3"><button type="submit" class="btn btn-default">提交</button><button type="reset" class="btn btn-default">重置</button></div></div></form>
</body>
</html>

CodeServlet:

public class CodeServlet extends HttpServlet {private static final long serialVersionUID = -8236507185410764108L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取sessionHttpSession session = req.getSession();//获取存储的验证码String idCodeText = (String) session.getAttribute("idCode");System.out.println("服务端验证码:" + idCodeText);String idCodeNum = req.getParameter("idCodeNum");System.out.println("输入的验证码:" + idCodeNum);//设置浏览器以UTF-8编码进行接收resp.setContentType("text/html;charset=UTF-8");PrintWriter out = resp.getWriter();if(idCodeText.equalsIgnoreCase(idCodeNum)){out.println("验证成功!");} else {out.println("验证码错误!");}}
}

Web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"id="WebApp_ID" version="3.0"><display-name>CookieAndSession</display-name><welcome-file-list><welcome-file>index.html</welcome-file></welcome-file-list><servlet><servlet-name>CodeServlet</servlet-name><servlet-class>com.zender.session.CodeServlet</servlet-class></servlet><servlet-mapping><servlet-name>CodeServlet</servlet-name><url-pattern>/CodeServlet.html</url-pattern></servlet-mapping><!-- 生成验证码的Servlet --><servlet><servlet-name>ValidateCode</servlet-name><servlet-class>org.jelly.image.ValidateCode</servlet-class></servlet><servlet-mapping><servlet-name>ValidateCode</servlet-name><url-pattern>/idCode</url-pattern></servlet-mapping>
</web-app>

这里使用了jelly-core-1.7.0.GA.jar来生成了验证码,具体使用方式请点击:jelly-core-1.7.0.GA.jar(http://www.blogjava.net/fancydeepin/archive/2014/08/03/jelly_image.html)

访问http://localhost:8081/CookieAndSession/index.jsp输入验证码,点击提交:

输入正确验证码,页面响应结果:

image

输入错误验证码,页面响应结果:

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; line-height: inherit; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">image</figcaption>

输入正确验证码,后台结果:

输入错误验证码,后台结果:

2,实现简易购物车

模拟一个数据库:

public class DB {private static HashMap<String, Book> hm = null;private DB(){}static{hm = new LinkedHashMap<String, Book>();Book book1 = new Book(1, "Java基础");Book book2 = new Book(2, "Oracle数据库");Book book3 = new Book(3, "C语言");Book book4 = new Book(4, "Python核心教程");Book book5 = new Book(5, "Web技术");hm.put(book1.getId() + "" , book1);hm.put(book2.getId() + "" , book2);hm.put(book3.getId() + "" , book3);hm.put(book4.getId() + "" , book4);hm.put(book5.getId() + "" , book5);}/*** 得到数据库中所有的书* @return*/public static HashMap<String, Book> getBooks(){return hm;}/*** 根据ID得到书* @param id* @return*/public static Book getBookById(String id){if(hm.containsKey(id)){return hm.get(id);}return null;}
}

BuyBookServlet这个Servlet用于购买图书:

//显示购买的图书
public class ShowMyCartServlet extends HttpServlet {private static final long serialVersionUID = -8236507185410764108L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html;charset=UTF-8");PrintWriter out = resp.getWriter();HttpSession session = req.getSession();List<Book> list = (ArrayList<Book>) session.getAttribute("books");if(list==null || list.size()==0){out.write("对不起,您还没有购买任何商品!!");return;}//显示用户买过的商品out.write("您买过如下商品:<br/>");for(Book book : list){out.write(book.getName() + "<br/>");}}
}

运行结果:

3,防止用户非法登录到某个页面

比如我们的用户管理系统,必须要登录成功后才能跳转到主页面,而不能直接绕过登录页面直接到主页面,这个应用是一个非常常见的应用。

当在验证用户的控制器LoginClServlet.java验证用户成功后,将当前的用户信息保存在Session对象中:

// 把user对象保存在sessionHttpSession session = req.getSession();session.setAttribute("loginUser", user);

然后在主页面Main.java最开始的地方,取出Session中的登录用户信息,如果信息为空,则为非法访问,直接跳转到登录页面,并提示相关信息:

// 取出login-user这个session
User loginUser = (User)req.getSession().getAttribute("loginUser");
if(loginUser == null){// 说明用户没有登录,让他跳转到登录页面req.setAttribute("error", "请登录!");req.getRequestDispatcher("/LoginServlet").forward(req,res);return;
}

那么这里就存在一个问题,一个网站会有很多个需要防止非法访问的页面,如果都是用这种方法岂不是很麻烦?

两种解决办法:

第一种:将这段验证用户的代码封装成函数,每次调用

第二种:使用过滤器

4,利用Session防止表单重复提交

具体的做法:

在服务器端生成一个唯一的随机标识号,专业术语称为Token(令牌),同时在当前用户的Session域中保存这个Token。然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。

在下列情况下,服务器程序将拒绝处理用户提交的表单请求:

1,存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。

2,当前用户的Session中不存在Token(令牌)。

3,用户提交的表单数据中没有Token(令牌)。

例如:

创建FormTokenServlet,用于生成Token和跳转到token.jsp页面:

public class FormTokenServlet extends HttpServlet {private static final long serialVersionUID = -8236507185410764108L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 创建令牌String token = TokenProccessor.getInstance().makeToken();System.out.println("在FormServlet中生成的token:" + token);// 在服务器使用session保存token(令牌)req.getSession().setAttribute("token", token);// 跳转到token.jsp页面req.getRequestDispatcher("/token.jsp").forward(req, resp);}
}

在token.jsp中使用隐藏域来存储Token(令牌),提交Token(令牌)到服务器:

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<%String path = request.getContextPath();String basePath = request.getScheme() + "://"+ request.getServerName() + ":" + request.getServerPort()+ path + "/";
%>
<html>
<head>
<base href="<%=basePath%>" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>表单提交</title>
<link href="css/bootstrap.css" rel="stylesheet">
<script src="js/jquery-3.2.1.js"></script>
<script src="js/bootstrap.js"></script>
</head>
<body><form class="form-horizontal" action="<%=request.getContextPath()%>/TokenServlet.html" role="form" method="post"><input type="hidden" name="token" value="<%=session.getAttribute("token") %>"><div class="form-group"><label for="firstname" class="col-sm-1 control-label">用戶名</label><div class="col-sm-3"><input type="text" class="form-control" name="idCodeNum"placeholder="请输入用戶名"></div></div><div class="form-group"><div class="col-sm-offset-1 col-sm-3"><button type="submit" class="btn btn-default">提交</button><button type="reset" class="btn btn-default">重置</button></div></div></form>
</body>
</html>

TokenServlet处理表单提交:

public class TokenServlet extends HttpServlet {private static final long serialVersionUID = -8236507185410764108L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {boolean b = false;String client_token = req.getParameter("token");// 如果用户提交的表单数据中没有token,则用户是重复提交了表单if (client_token == null) {b = true;}// 取出存储在Session中的tokenString serverToken = (String) req.getSession().getAttribute("token");// 如果当前用户的Session中不存在Token,则用户是重复提交了表单if (serverToken == null) {b = true;}// 存储在Session中的Token与表单提交的Token不同,则用户是重复提交了表单if (!client_token.equals(serverToken)) {b = true;}if (b == true) {System.out.println("请不要重复提交!");return;}// 移除session中的tokenreq.getSession().removeAttribute("token");System.out.println("正在处理用户提交请求!!");}
}

运行结果如下:

十、用户禁用Cookie后的Session处理

这里存在一种情况,假如用户浏览器禁用了Cookie怎么办?比如我把Chrome的Cookie禁用,如下:

解决方法:URL重写

Servlet中的response提供了对URL重写的方法:

response.encodeRedirectURL(String url)用于对sendRedirect方法后的url地址进行重写response.encodeURL(String url)用于对表单action和超链接的url地址进行重写

那么URL重写是什么意思呢?其实就是人为地把JSESSIONID附在了url的后面,比如我们修改之前写的简易购物车,ShowBook中所有的点击购买超链接都要重写。

之前我们是这么写的:

out.println("<tr><td>"+book.getName()+"</td><td><a href='"+ req.getContextPath() +"/BuyBookServlet.html?id="+book.getId()+"'>点击购买</a></td></tr>");

现在进行URL重写:

req.getSession();String url = "/MyCart/BuyBookCl?id="+book.getId();url = resp.encodeURL(url);out.println("<tr><td>"+book.getName()+"</td><td><a href='"+url+"'>点击购买</a></td></tr>");

需要注意的是,重写之前一定要调用或者确保使用过request.getSession()这个方法。

重写之前,访问ShowBookServlet的源代码是这样的:

重写之后呢:

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; line-height: inherit; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">image</figcaption>

可以看到URL重写之后,jsessionid这个参数自动附在了url后面,由此,得以确保我们的Session在Cookie被禁用的情况下继续正常使用。这时候我们查看购物车的地址栏如下,可以明显看到jsessionid这个参数:

原文出处:作者:Zender

原文链接:https://www.cnblogs.com/Zender/p/7657516.html

asp.net 获取客户端cookie_开发中你一定碰到过的cookie和session问题,今天一并帮你解决!...相关推荐

  1. Asp.Net获取客户端信息-学习中

    学习中.高手飘过吧. 前台代码: <form id="form1" runat="server">     <div>     < ...

  2. ASP.NET AJAX 在Web开发中的应用

    摘 要 ASP.NET AJAX 实现了Web页面丰富的部分刷新效果.本文通过介绍AJAX原理,引申到ASP.NET AJAX原理,并总结了在Web 开发应用中要注意的若干问题.合理地利用ASP.NE ...

  3. ASP.NET获取客户端IP及MAC地址

    ASP.NET获取客户端IP及MAC地址 2008-12-01 14:50 ASP.NET获取客户端IP及MAC地址很多时候非常必要的,像做投票.留言.评论 等系统的时候防止别人恶意操作,导致数据的虚 ...

  4. ASP.NET获取客户端、服务器端基础信息集合

    1. 在ASP.NET中专用属性: 获取服务器电脑名:Page.Server.ManchineName 获取用户信息:Page.User 获取客户端电脑名:Page.Request.UserHostN ...

  5. ASP.NET获取客户端、服务器端基础信息

    1. 在ASP.NET中专用属性: 获取服务器电脑名:Page.Server.ManchineName 获取用户信息:Page.User 获取客户端电脑名:Page.Request.UserHostN ...

  6. android中获取版本号,Android 开发中如何获取版本号

    android 开发中如何获取版本号. 1.获取版本名,版本号: /** * 获取版本号 * * @return * @throws exception */ private int getversi ...

  7. 开发中经常碰到的问题cookie和session问题,今天一并解决

    点击上方"好好学java",选择"置顶公众号" 优秀学习资源.干货第一时间送达! 精彩内容 java实战练习项目教程 2018微服务资源springboot.s ...

  8. 这可能是前端开发中能遇到最全的cookie问题了

    theme: cyanosis 前言 什么是 cookie HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据.浏览器会存储 ...

  9. ASP如何获取客户端真实IP地址

    在ASP中使用 Request.ServerVariables("REMOTE_ADDR") 来取得客户端的IP地址, 但如果客户端是使用代理服务器来访问,那取到的就是代理服务器的 ...

最新文章

  1. Django 错误汇总
  2. 批命令 set /a与set /p有哪些区别
  3. 图解半监督学习FixMatch,只用10张标注图片训练CIFAR10
  4. Android单元测试全解
  5. python大数据分析实例-python大数据分析代码案例
  6. git远程版本硬删除
  7. VBA中访问Web Service
  8. NLog-ASP.NET Core 5入门
  9. 谷粒商城--API三级分类--网关统一配置跨域
  10. PHP array_pop()函数与示例
  11. 条件概率与条件概率举例
  12. 链接mysql工具封装_JDBC——java连接MySQL封装处理
  13. 给互联网人的反侦查手册
  14. 破解,汉化,越狱,解锁,为什么中国的大神总是“昙花一现”?
  15. 压缩/解压缩库ZipArchive
  16. 「干货」那些必知的物联网行业术语
  17. EGO Planner代码解析bspline_optimizer部分(3)
  18. 计算机程序运行异常处理,Win7电脑运行程序出现appcrash错误怎么解决?
  19. Altium Designer入门
  20. 服务器安全设置全攻略

热门文章

  1. 【Elasticsearch】腾讯Elasticsearch海量规模背后的内核优化剖析
  2. 【Elasticsearch】使用 Elasticsearch Freeze index API 创建冻结索引
  3. 【Java】java中的Fork/Join
  4. 60-300-022-使用-延迟数据-Flink中allowedLateness详细介绍
  5. 【SpringMVC】SpringMVC :@RequestMapping注解
  6. scala学习-Linux命令行运行jar包传入main方法参数
  7. scala学习-scala读取Hbase表中数据并且做join连接查询
  8. Spark学习:spark读取HBase数据报异常java.io.NotSerializableException
  9. 计算尖峰电流的目的_183 新能源汽车电机控制器母线电容容值如何计算?
  10. 查看工作组计算机没有服务器,计算机相关:网上邻居问题:当前工作组的服务器列表无法使用...