Servlet第六篇【Session介绍、API、生命周期、应用、与Cookie区别】

什么是Session

Session 是另一种记录浏览器状态的机制。不同的是Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息以某种的形式记录在服务器,这就是Session

如果说Cookie是检查用户身上的”通行证“来确认用户的身份,那么Session就是通过检查服务器上的”客户明细表“来确认用户的身份的。Session相当于在服务器中建立了一份“客户明细表”。

为什么要使用Session技术?

Session比Cookie使用方便,Session可以解决Cookie解决不了的事情【Session可以存储对象,Cookie只能存储字符串。】。

Session API

  • long getCreationTime();【获取Session被创建时间】
  • String getId();【获取Session的id】
  • long getLastAccessedTime();【返回Session最后活跃的时间】
  • ServletContext getServletContext();【获取ServletContext对象】
  • void setMaxInactiveInterval(int var1);【设置Session超时时间】
  • int getMaxInactiveInterval();【获取Session超时时间】
  • Object getAttribute(String var1);【获取Session属性】
  • Enumeration getAttributeNames();【获取Session所有的属性名】
  • void setAttribute(String var1, Object var2);【设置Session属性】
  • void removeAttribute(String var1);【移除Session属性】
  • void invalidate();【销毁该Session】
  • boolean isNew();【该Session是否为新的】

Session作为域对象

从上面的API看出,Session有着request和ServletContext类似的方法。其实Session也是一个域对象。Session作为一种记录浏览器状态的机制,只要Session对象没有被销毁,Servlet之间就可以通过Session对象实现通讯

我们来试试吧,在Servlet4中设置Session属性

//得到Session对象HttpSession httpSession = request.getSession();//设置Session属性httpSession.setAttribute("name", "看完博客就要点赞!!");

在Servlet5中获取到Session存进去的属性

//获取到从Servlet4的Session存进去的值HttpSession httpSession = request.getSession();String value = (String) httpSession.getAttribute("name");System.out.println(value);

访问Servlet4,再访问Servlet5
在这里插入图片描述
一般来讲,当我们要存进的是用户级别的数据就用Session,那什么是用户级别呢?只要浏览器不关闭,希望数据还在,就使用Session来保存。

Session的生命周期和有效期

Session在用户第一次访问服务器Servlet,jsp等动态资源就会被自动创建,Session对象保存在内存里,这也就为什么上面的例子可以直接使用request对象获取得到Session对象。

如果访问HTML,IMAGE等静态资源Session不会被创建。

Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,无论是否对Session进行读写,服务器都会认为Session活跃了一次。

由于会有越来越多的用户访问服务器,因此Session也会越来越多。为了防止内存溢出,服务器会把长时间没有活跃的Session从内存中删除,这个时间也就是Session的超时时间。

Session的超时时间默认是30分钟,有三种方式可以对Session的超时时间进行修改

第一种方式:在tomcat/conf/web.xml文件中设置,时间值为20分钟,所有的WEB应用都有效

            <session-config><session-timeout>20</session-timeout></session-config>    

在这里插入图片描述
第二种方式:在单个的web.xml文件中设置,对单个web应用有效,如果有冲突,以自己的web应用为准。

            <session-config><session-timeout>20</session-timeout></session-config>    

第三种方式:通过setMaxInactiveInterval()方法设置

//设置Session最长超时时间为60秒,这里的单位是秒httpSession.setMaxInactiveInterval(60);System.out.println(httpSession.getMaxInactiveInterval());

在这里插入图片描述
Session的有效期与Cookie的是不同的

在这里插入图片描述

使用Session完成简单的购物功能

我们还是以书籍为例,所以可以copy“显示浏览过的商品“例子部分的代码。

response.setContentType("text/html;charset=UTF-8");PrintWriter printWriter = response.getWriter();printWriter.write("网页上所有的书籍:" + "<br/>");//拿到数据库所有的书LinkedHashMap<String, Book> linkedHashMap = DB.getAll();Set<Map.Entry<String, Book>> entry = linkedHashMap.entrySet();//显示所有的书到网页上for (Map.Entry<String, Book> stringBookEntry : entry) {Book book = stringBookEntry.getValue();String url = "/ouzicheng/Servlet6?id=" + book.getId();printWriter.write(book.getName());printWriter.write("<a href='" + url + "'>购买</a>");printWriter.write("<br/>");}

在购物车页面上,获取到用户想买的书籍【用户可能不单想买一本书,于是乎,就用一个List容器装载书籍】,有了:先遍历Cookie,再判断是否是第一次访问Servlet的逻辑思路,我们就可以先获取到Session的属性,如果Session的属性为null,那么就是还没有该属性

//得到用户想买书籍的idString id = request.getParameter("id");//根据书籍的id找到用户想买的书Book book = (Book) DB.getAll().get(id);//获取到Session对象HttpSession httpSession = request.getSession();//由于用户可能想买多本书的,所以我们用一个容器装着书籍List list = (List) httpSession.getAttribute("list");if (list == null) {list = new ArrayList();//设置Session属性httpSession.setAttribute("list",list);}//把书籍加入到list集合中list.add(book);

按我们正常的逻辑思路:先创建一个ArrayList对象,把书加到list集合中,然后设置Session的属性。这样是行不通的。每次Servlet被访问的时候都会创建一个ArrayList集合,书籍会被分发到不同的ArrayList中去。所以下面的代码是不行的!

//得到用户想买书籍的idString id = request.getParameter("id");//根据书籍的id找到用户想买的书Book book = (Book) DB.getAll().get(id);//获取到Session对象HttpSession httpSession = request.getSession();//创建List集合List list = new ArrayList();list.add(book);httpSession.setAttribute("list", list);

既然用户已经购买了书籍,那么也应该给提供页面显示用户购买过哪些书籍

//得到用户想买书籍的idString id = request.getParameter("id");//根据书籍的id找到用户想买的书Book book = (Book) DB.getAll().get(id);//获取到Session对象HttpSession httpSession = request.getSession();//由于用户可能想买多本书的,所以我们用一个容器装着书籍List list = (List) httpSession.getAttribute("list");if (list == null) {list = new ArrayList();//设置Session属性httpSession.setAttribute("list",list);}//把书籍加入到list集合中list.add(book);String url = "/ouzicheng/Servlet7";response.sendRedirect(url);

列出用户购买过的书籍

//要得到用户购买过哪些书籍,得到Session的属性遍历即可HttpSession httpSession = request.getSession();List<Book> list = (List) httpSession.getAttribute("list");if (list == null || list.size() == 0) {printWriter.write("对不起,你还没有买过任何商品");} else {printWriter.write("您购买过以下商品:");printWriter.write("<br/>");for (Book book : list) {printWriter.write(book.getName());printWriter.write("<br/>");}}

效果如下
在这里插入图片描述

Session的实现原理

用现象说明问题,我在Servlet4中的代码设置了Session的属性

//得到Session对象HttpSession httpSession = request.getSession();//设置Session属性httpSession.setAttribute("name", "看完博客就要点赞!!");

接着在Servlet7把Session的属性取出来

String value = (String) request.getSession().getAttribute("name");printWriter.write(value);

自然地,我们能取到在Servlet4中Session设置的属性
在这里插入图片描述
接着,我在浏览器中新建一个会话,再次访问Servlet7
在这里插入图片描述
发现报了空指针异常的错误
在这里插入图片描述
现在问题来了:服务器是如何实现一个session为一个用户浏览器服务的?换个说法:为什么服务器能够为不同的用户浏览器提供不同session?

HTTP协议是无状态的,Session不能依据HTTP连接来判断是否为同一个用户。于是乎:服务器向用户浏览器发送了一个名为JESSIONID的Cookie,它的值是Session的id值。其实Session依据Cookie来识别是否是同一个用户。

简单来说:Session 之所以可以识别不同的用户,依靠的就是Cookie

该Cookie是服务器自动颁发给浏览器的,不用我们手工创建的。该Cookie的maxAge值默认是-1,也就是说仅当前浏览器使用,不将该Cookie存在硬盘中

我们来捋一捋思路流程:当我们访问Servlet4的时候,服务器就会创建一个Session对象,执行我们的程序代码,并自动颁发个Cookie给用户浏览器
在这里插入图片描述

当我们用同一个浏览器访问Servlet7的时候,浏览器会把Cookie的值通过http协议带过去给服务器,服务器就知道用哪一Session。
在这里插入图片描述
而当我们使用新会话的浏览器访问Servlet7的时候,该新浏览器并没有Cookie,服务器无法辨认使用哪一个Session,所以就获取不到值

浏览器禁用了Cookie,Session还能用吗?

上面说了Session是依靠Cookie来识别用户浏览器的。如果我的用户浏览器禁用了Cookie了呢?绝大多数的手机浏览器都不支持Cookie,那我的Session怎么办?

在这里插入图片描述
好的,我们来看看情况是怎么样的。用户浏览器访问Servlet4的时候,服务器向用户浏览器颁发了一个Cookie
在这里插入图片描述
但是呢,当用户浏览器访问Servlet7的时候,由于我们禁用了Cookie,所以用户浏览器并没有把Cookie带过去给服务器。
在这里插入图片描述

一看,Session好像不能用了。但是Java Web提供了解决方法:URL地址重写

HttpServletResponse类提供了两个URL地址重写的方法:

  • encodeURL(String url)
  • encodeRedirectURL(String url)

需要值得注意的是:这两个方法会自动判断该浏览器是否支持Cookie,如果支持Cookie,重写后的URL地址就不会带有jsessionid了【当然了,即使浏览器支持Cookie,第一次输出URL地址的时候还是会出现jsessionid(因为没有任何Cookie可带)】

下面我们就以上面“购物”的例子来做试验吧!首先我们来看看禁用掉Cookie对原来的小例子有什么影响。

访问Servlet1,随便点击一本书籍购买

在这里插入图片描述

无论点击多少次,都会直接提示我们有买过任何商品
在这里插入图片描述
原因也非常简单,没有Cookie传递给服务器,服务器每次创建的时候都是新的Session,导致最后获取到的List集合一定是空的。

不同Servlet获取到的Session的id号都是不同的。
在这里插入图片描述
下面我们就对URL进行重写,看看能不能恢复没有禁掉Cookie之前的效果。

原则:把Session的属性带过去【传递给】另外一个Servlet,都要URL地址重写

在跳转到显示购买过商品的Servlet的时候,URL地址重写。

String url = "/ouzicheng/Servlet7";response.sendRedirect(response.encodeURL(url));

再次访问Servlet1,当我点击javaweb的时候,已经能够成功出现我买过的商品了。并且Session的id通过URL地址重写,使用的是同一个Session
在这里插入图片描述
在这里插入图片描述
URL地址重写的原理:将Session的id信息重写到URL地址中。服务器解析重写后URL,获取Session的id。这样一来,即使浏览器禁用掉了Cookie,但Session的id通过服务器端传递,还是可以使用Session来记录用户的状态。

Session禁用Cookie

Java Web规范支持通过配置禁用Cookie

禁用自己项目的Cookie

  • 在META-INF文件夹下的context.xml文件中修改(没有则创建)
       <?xml version='1.0' encoding='utf-8'?><Context path="/ouzicheng" cookies="false"></Context>

禁用全部web应用的Cookie

  • 在conf/context.xml中修改

注意:该配置只是让服务器不能自动维护名为jsessionid的Cookie,并不能阻止Cookie的读写。

Session案例

使用Session完成用户简单登陆

先创建User类

private String username = null;private String password = null;public User() {}public User(String username, String password) {this.username = username;this.password = password;}....各种set、get方法

使用简单的集合模拟一个数据库

private static List<User> list = new ArrayList<>();//装载些数据进数据库static {list.add(new User("aaa","111"));list.add(new User("bbb","222"));list.add(new User("ccc","333"));}//通过用户名和密码查找用户public static User find(String username, String password) {for (User user : list) {if (user.getUsername().equals(username) && user.getPassword().equals(password)) {return user;}}return null;}

表单提交的工作我就在jsp写了,如果在Servlet写太麻烦了!


<form action="/ouzicheng/LoginServlet" method="post">用户名:<input type="text" name="username"><br/>密码:<input type="password" name="password"><br/><input type="submit" value="提交">
</form>

获取到表单提交的数据,查找数据库是否有相对应的用户名和密码。如果没有就提示用户名或密码出错了,如果有就跳转到另外一个页面

String username = request.getParameter("username");String password = request.getParameter("password");User user = UserDB.find(username, password);//如果找不到,就是用户名或密码出错了。if (user == null) {response.getWriter().write("you can't login");return;}//标记着该用户已经登陆了!HttpSession httpSession = request.getSession();httpSession.setAttribute("user", user);//跳转到其他页面,告诉用户成功登陆了。response.sendRedirect(response.encodeURL("index.jsp"));

我们来试试下数据库没有的用户名和密码,提示我不能登陆。
在这里插入图片描述
在这里插入图片描述
试试数据库存在的用户名和密码

在这里插入图片描述
在这里插入图片描述

利用Session防止表单重复提交

重复提交的危害:

  • 在投票的网页上不停地提交,实现了刷票的效果。
  • 注册多个用户,不断发帖子,扰乱正常发帖秩序。

首先我们来看一下常见的重复提交。

  • 在处理表单的Servlet中刷新。
  • 后退再提交
  • 网络延迟,多次点击提交按钮

下面的gif是后退再提交,在处理提交请求的Servlet中刷新
在这里插入图片描述
下面的gif是网络延迟,多次点击提交按钮

在这里插入图片描述

对于网络延迟造成的多次提交数据给服务器,其实是客户端的问题。于是,我们可以使用javaScript来防止这种情况

要做的事情也非常简单:当用户第一次点击提交按钮时,把数据提交给服务器。当用户再次点击提交按钮时,就不把数据提交给服务器了。

监听用户提交事件。只能让用户提交一次表单!


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>表单提交</title><script type="text/javascript">//定义一个全局标识量:是否已经提交过表单数据var isCommitted = false;function doSubmit() {//false表示的是没有提交过,于是就可以让表单提交给Servletif(isCommitted==false) {isCommitted = true;return true;}else {return false;}}</script>
</head>
<body><form action="/ouzicheng/Servlet7" onsubmit="return doSubmit()">用户名:<input type="text" name="username"><input type="submit" value="提交">
</form></body>
</html>

好的,我们来试一下是不是真的可以解决网络延迟所造成的多次提交表单数据,注意鼠标,我已经点击过很多次的了!

在这里插入图片描述

由于网络延迟造成的多次提交数据给服务器,我们还可以使用javaScript代码这样解决:当我点击过一次提交按钮时,我就把提交的按钮隐藏起来。不能让用户点击了!

想要让按钮隐藏起来,也很简单。只要获取到按钮的节点,就可以控制按钮的隐藏或显示了!

<script type="text/javascript">function doSubmit() {var button = document.getElementById("button");button.disabled = disabled;return true;}</script>

我们再来看一下效果
在这里插入图片描述
在处理表单的Servlet中刷新和后退再提交这两种方式不能只靠客户端来限制了。也就是说javaScript代码无法阻止这两种情况的发生。

于是乎,我们就想得用其他办法来阻止表单数据重复提交了。我们现在学了Session,Session可以用来标识一个用户是否登陆了。Session的原理也说了:不同的用户浏览器会拥有不同的Session。而request和ServletContext为什么就不行呢?request的域对象只能是一次http请求,提交表单数据的时候request域对象的数据取不出来。ServletContext代表整个web应用,如果有几个用户浏览器同时访问,ServletContext域对象的数据会被多次覆盖掉,也就是说域对象的数据就毫无意义了。

可能到这里,我们会想到:在提交数据的时候,存进Session域对象的数据,在处理提交数据的Servlet中判断Session域对象数据???。究竟判断Session什么?判断Session域对象的数据不为null?没用呀,既然已经提交过来了,那肯定不为null。

此时,我们就想到了,在表单中还有一个隐藏域,可以通过隐藏域把数据交给服务器。

  • 判断Session域对象的数据和jsp隐藏域提交的数据是否对应。
  • 判断隐藏域的数据是否为空【如果为空,就是直接访问表单处理页面的Servlet】
  • 判断Session的数据是否为空【servlet判断完是否重复提交,最好能立马移除Session的数据,不然还没有移除的时候,客户端那边儿的请求又来了,就又能匹配了,产生了重复提交。如果Session域对象数据为空,证明已经提交过数据了!】

我们向Session域对象的存入数据究竟是什么呢?简单的一个数字?好像也行啊。因为只要Session域对象的数据和jsp隐藏域带过去的数据对得上号就行了呀,反正在Servlet上判断完是否重复提交,会立马把Session的数据移除掉的。更专业的做法是:向Session域对象存入的数据是一个随机数【Token–令牌】。

生成一个独一无二的随机数


/*
* 产生随机数就应该用一个对象来生成,这样可以避免随机数的重复。
* 所以设计成单例
* */
public class TokenProcessor {private TokenProcessor() {}private final static TokenProcessor TOKEN_PROCESSOR = new TokenProcessor();public static TokenProcessor getInstance() {return TOKEN_PROCESSOR;}public static String makeToken() {//这个随机生成出来的Token的长度是不确定的String token = String.valueOf(System.currentTimeMillis() + new Random().nextInt(99999999));try {//我们想要随机数的长度一致,就要获取到数据指纹MessageDigest messageDigest = MessageDigest.getInstance("md5");byte[] md5 = messageDigest.digest(token.getBytes());//如果我们直接 return  new String(md5)出去,得到的随机数会乱码。//因为随机数是任意的01010101010,在转换成字符串的时候,会查gb2312的码表,gb2312码表不一定支持该二进制数据,得到的就是乱码//于是乎经过base64编码成了明文的数据BASE64Encoder base64Encoder = new BASE64Encoder();return base64Encoder.encode(md5);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return null;}}

创建Token随机数,并跳转到jsp页面

//生出随机数TokenProcessor tokenProcessor = TokenProcessor.getInstance();String token = tokenProcessor.makeToken();//将随机数存进Session中request.getSession().setAttribute("token", token);//跳转到显示页面request.getRequestDispatcher("/login.jsp").forward(request, response);

jsp隐藏域获取到Session的值

<form action="/ouzicheng/Servlet7" >用户名:<input type="text" name="username"><input type="submit" value="提交" id="button"><%--使用EL表达式取出session中的Token--%><input type="hidden" name="token" value="${token}" >
</form>

在处理表单提交页面中判断:jsp隐藏域是否有值带过来,Session中的值是否为空,Session中的值和jsp隐藏域带过来的值是否相等

String serverValue = (String) request.getSession().getAttribute("token");String clientValue = request.getParameter("token");if (serverValue != null && clientValue != null && serverValue.equals(clientValue)) {System.out.println("处理请求");//清除Session域对象数据request.getSession().removeAttribute("token");}else {System.out.println("请不要重复提交数据!");}

下面我们再来看一下,已经可以解决表单重复提交的问题了!
在这里插入图片描述

实现原理是非常简单的:

  • 在session域中存储一个token
  • 然后前台页面的隐藏域获取得到这个token
  • 在第一次访问的时候,我们就判断seesion有没有值,如果有就比对。对比正确后我们就处理请求,接着就把session存储的数据给删除了
  • 等到再次访问的时候,我们session就没有值了,就不受理前台的请求了!

一次性校验码

  • 一次性校验码其实就是为了防止暴力猜测密码
  • 在讲response对象的时候,我们使用response对象输出过验证码,但是没有去验证!
  • 验证的原理也非常简单:生成验证码后,把验证码的数据存进Session域对象中,判断用户输入验证码是否和Session域对象的数据一致。
  • 生成验证码图片,并将验证码存进Session域中
        //在内存中生成图片BufferedImage bufferedImage = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);//获取到这张图片Graphics2D graphics2D = (Graphics2D) bufferedImage.getGraphics();//设置背景色为白色graphics2D.setColor(Color.white);graphics2D.fillRect(0, 0, 80, 20);//设置图片的字体和颜色graphics2D.setFont(new Font(null, Font.BOLD, 20));graphics2D.setColor(Color.BLUE);//生成随机数String randomNum = makeNum();//往这张图片上写数据,横坐标是0,纵坐标是20graphics2D.drawString(randomNum, 0, 20);//将随机数存进Session域中request.getSession().setAttribute("randomNum", randomNum);//控制浏览器不缓存该图片response.setHeader("Expires", "-1");response.setHeader("Cache-Control", "no-cache");response.setHeader("Pragma", "no-cache");//通知浏览器以图片的方式打开response.setHeader("Content-type", "image/jpeg");//把图片写给浏览器ImageIO.write(bufferedImage, "jpg", response.getOutputStream());

生成随机数的方法:

private String makeNum() {Random random = new Random();//生成0-6位的随机数int num = random.nextInt(999999);//验证码的数位全都要6位数,于是将该随机数转换成字符串,不够位数就添加String randomNum = String.valueOf(num);//使用StringBuffer来拼凑字符串StringBuffer stringBuffer = new StringBuffer();for (int i = 0; i < 6 - randomNum.length(); i++) {stringBuffer.append("0");}return stringBuffer.append(randomNum).toString();}

jsp显示页面


<form action="/ouzicheng/Login2Servlet">用户名:<input type="text" name="username"><br>密码:<input type="password" name="password"><br>验证码:<input type="text" name="randomNum"><img src="/ouzicheng/ImageServlet" ><br><br><input type="submit" value="提交"></form>

处理提交表单数据的Servlet,判断用户带过来验证码的数据是否和Session的数据相同。

//获取用户输入验证码的数据String client_randomNum = request.getParameter("randomNum");//获取Session中的数据String session_randomNum = (String) request.getSession().getAttribute("randomNum");//判断他俩数据是否相等,用户是否有输入验证码,Session中是否为空if (client_randomNum == null || session_randomNum == null || !client_randomNum.equals(session_randomNum)) {System.out.println("验证码错误了!!!");return ;}//下面就是验证用户名和密码...................

显示页面是这样子的
在这里插入图片描述
我们来看一下效果!
在这里插入图片描述
对于校验码实现思路是这样子的:

  • 使用awt语法来描写一张验证码,生成随机数保存在seesion域中,我们让验证码不能缓存起来【做到验证码都不一样】
  • 页面直接访问Servlet来获取我们的验证码,于是我们验证码的值就会改变【同时session的值也会被改变】
  • 当用户验证的时候,就是session内的值的验证了。

Session和Cookie的区别

从存储方式上比较

  • Cookie只能存储字符串,如果要存储非ASCII字符串还要对其编码。
  • Session可以存储任何类型的数据,可以把Session看成是一个容器

从隐私安全上比较

  • Cookie存储在浏览器中,对客户端是可见的。信息容易泄露出去。如果使用Cookie,最好将Cookie加密
  • Session存储在服务器上,对客户端是透明的。不存在敏感信息泄露问题。

从有效期上比较

  • Cookie保存在硬盘中,只需要设置maxAge属性为比较大的正整数,即使关闭浏览器,Cookie还是存在的
  • Session的保存在服务器中,设置maxInactiveInterval属性值来确定Session的有效期。并且Session依赖于名为JSESSIONID的Cookie,该Cookie默认的maxAge属性为-1。如果关闭了浏览器,该Session虽然没有从服务器中消亡,但也就失效了。

从对服务器的负担比较

  • Session是保存在服务器的,每个用户都会产生一个Session,如果是并发访问的用户非常多,是不能使用Session的,Session会消耗大量的内存。
  • Cookie是保存在客户端的。不占用服务器的资源。像baidu、Sina这样的大型网站,一般都是使用Cookie来进行会话跟踪。

从浏览器的支持上比较

  • 如果浏览器禁用了Cookie,那么Cookie是无用的了!
  • 如果浏览器禁用了Cookie,Session可以通过URL地址重写来进行会话跟踪。

从跨域名上比较

  • Cookie可以设置domain属性来实现跨域名
  • Session只在当前的域名内有效,不可夸域名

Cookie和Session共同使用

如果仅仅使用Cookie或仅仅使用Session可能达不到理想的效果。这时应该尝试一下同时使用Session和Cookie

那么,什么时候才需要同时使用Cookie和Session呢?

在上一篇博客中,我们使用了Session来进行简单的购物,功能也的确实现了。现在有一个问题:我在购物的途中,不小心关闭了浏览器。当我再返回进去浏览器的时候,发现我购买过的商品记录都没了!!为什么会没了呢?原因也非常简单:服务器为Session自动维护的Cookie的maxAge属性默认是-1的,当浏览器关闭掉了,该Cookie就自动消亡了。当用户再次访问的时候,已经不是原来的Cookie了。

我们现在想的是:即使我不小心关闭了浏览器了,我重新进去网站,我还能找到我的购买记录。

要实现该功能也十分简单,问题其实就在:服务器为Session自动维护的Cookie的maxAge属性是-1,Cookie没有保存在硬盘中。我现在要做的就是:把Cookie保存在硬盘中,即使我关闭了浏览器,浏览器再次访问页面的时候,可以带上Cookie,从而服务器识别出Session。

第一种方式:只需要在处理购买页面上创建Cookie,Cookie的值是Session的id返回给浏览器即可

Cookie cookie = new Cookie("JSESSIONID",session.getId());cookie.setMaxAge(30*60);cookie.setPath("/ouzicheng/");response.addCookie(cookie);

第二种方式: 在server.xml文件中配置,将每个用户的Session在服务器关闭的时候序列化到硬盘或数据库上保存。但此方法不常用,知道即可!

下面看一下效果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/557175.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 异步阻塞_大白话搞懂什么是同步/异步/阻塞/非阻塞

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼前言在最近的一些面试中&#xff0c;跟应聘者聊了比较多关于“同步/异步&#xff0c;阻塞/非阻塞”相关的话题&#xff0c;发现大家对于这些概念的理解都比较模糊&#xff0c;甚至有的同学会反问“他们不就是同一个东西吗&#xff…

python 3.9.0a0_Python 3.9.0 稳定版发布

IT之家10月6日消息据网友投递&#xff0c;Python 3.9.0 稳定版(Python 3.9.0 final)昨日正式发布&#xff0c;它包含许多新功能和优化&#xff0c;禁止在Windows 7上安装&#xff0c;且默认提供64位安装程序。IT之家了解到&#xff0c;Python 3.9.0 alpha 1首个迭代版本于2019 …

Tomcat+Servlet面试题都在这里

下面是我整理下来的Servlet知识点: 图上的知识点都可以在我其他的文章内找到相应内容。 Tomcat常见面试题 Tomcat的缺省端口是多少&#xff0c;怎么修改 找到Tomcat目录下的conf文件夹进入conf文件夹里面找到server.xml文件打开server.xml文件在server.xml文件里面找到下列…

java sdk下载_Java Sdk下载 | 保利威帮助中心

播放界面开发前准备1.小程序微信开发者后台设置-开发设置-服务器域名中配置 [request合法域名]开始开发1.获取频道直播播放地址index.wxmlindex.js选项说明uid类型&#xff1a;String说明&#xff1a;直播账户idvid类型&#xff1a;String说明&#xff1a;直播频道idvideoConte…

mysql union 与 union all 语法及用法

1.mysql union 语法 mysql union 用于把来自多个select 语句的结果组合到一个结果集合中。语法为&#xff1a; select column,......from table1union [all]select column,...... from table2...在多个select 语句中&#xff0c;对应的列应该具有相同的字段属性&#xff0c…

java反射 获取方法参数名_java 反射借助 asm 获取参数名称最优雅简单的方式

背景说明最近写反射相关的代码&#xff0c;想获取对应的参数名称&#xff0c;却发现没有特别好的方式。jdk7 及其以前&#xff0c;是无法通过反射获取参数名称的。jdk8 可以获取&#xff0c;但是要求指定 -parameter 启动参数&#xff0c;限制较多。期间尝试过类似于 Mybatis 使…

Golang基础知识入门详解

Go语言入门 Go语言入门教程 很多人将 Go 语言 称为 21 世纪的 C 语言&#xff0c;因为 Go 不仅拥有 C 语言的简洁和性能&#xff0c;而且还很好的提供了 21 世纪互联网环境下服务端开发的各种实用特性&#xff0c;让开发者在语言级别就可以方便的得到自己想要的东西。 在 Go…

controller层和service层的作用

1.在controller和service里都写那些代码&#xff1f; Controller&#xff0c;从字面上理解是控制器&#xff0c;所以它是负责业务调度的&#xff0c;所以在这一层应写一些业务的调度代码&#xff0c;而具体的业务处理应放在service中去写&#xff0c;而且service不单纯是对于d…

udp模拟tcp java_Java简单实现UDP和TCP

TCP实现TCP协议需要在双方之间建立连接&#xff0c;通过输入输出流来进行数据的交换&#xff0c;建立需要通过三次握手&#xff0c;断开需要四次挥手&#xff0c;保证了数据的完整性&#xff0c;但传输效率也会相应的降低。简单的TCP实现//服务端public class TcpServer {publi…

为什么Controller层注入的是Service接口,而不是ServiceImpl实现类

错误代码&#xff1a; Service层接口interface PCI{} 接口实现类Serviceclass PCIImpt imeplements PCI{}Controller层Autowiredprivate PCIImpt pciImpt; //注入了实现类在没有使用maven管理的时候注入实现类编译一直未报错&#xff0c;也就是说编译可以通过&#xff0c;但…

ant java 返回_使用Ant自动化我们的java项目生成

现在我们已经了解如何定义属性、依赖关系以及如何运行ant&#xff0c;接下来我们将学习怎样使用ant编译java源代码并生成jar文件。编译源代码由于Ant的主要目标就是生成java应用程序&#xff0c;它内置了javac任务来调用java的编译器。此任务一般定义如下Ant会寻找src目录下所有…

Spring boot自定义启动字符画(banner)

其实很好改&#xff0c;只需要在resources下新建一个txt文件就可以&#xff0c;命名为banner.txt&#xff0c;那这种字符该怎么拼出来呢&#xff0c;下面推荐一个网址&#xff0c;有这种工具&#xff0c;链接传送门&#xff1a;打开传送门 直接输入要生成的字母&#xff0c;系…

模拟天天酷跑游戏java_cocos2d 简单高仿天天酷跑游戏

1.先直接上视频来看下这个游戏的样子(GIF已经不能满足这个游戏的展示了)跑酷游戏最纠结的是地图&#xff0c;碰撞倒是简单&#xff0c;可以自己写或者使用box2d等物理引擎。跑酷游戏地图的特点就是随机性。但是随机中又有策划特意安排的部分&#xff0c;这样让玩家有小小惊喜。…

mybatis 一对多查询 按结果嵌套处理、按查询嵌套处理,以及两者之间的区别

mybatis 一对多查询 按结果嵌套处理、按查询嵌套处理 最近用到一对多查询&#xff0c;记录一下 实体类 public class RegionEntity implements Serializable {private Long rid;private String regionName;private String addrImage;private String type;private List<Fl…

java 复制字段_java - 在构造函数中按字段复制字段 - 我需要一个更简洁的形式 - SO中文参考 - www.soinside.com...

你能做的是&#xff1a;public MyClassExtension(MyClass instance) {super(...);new_field some_value;}在创建子类时&#xff0c;最好调用超级构造函数(否则默认情况下将调用默认的超级构造函数)。为什么这样&#xff1f;因为它限制了重新定义相同初始化所需的次数。例如&am…

@Transactional 实现原理

1、简介 Transactional是spring中定义的事务注解&#xff0c;在方法或类上加该注解开启事务。主要是通过反射获取bean的注解信息&#xff0c;利用AOP对编程式事务进行封装实现。(spring-5.1.8.RELEASE) Target({ElementType.TYPE, ElementType.METHOD}) Retention(RetentionP…

在java中jvm目录_JVM具体在哪个文件夹下的

\jdk1.6.0 -- JDK的根目录&#xff0c;包含一些软件版权&#xff0c;声明&#xff0c;和自述文件&#xff0c;同时包含归档了的Java平台源代码包src.zip\jdk1.6.0\bin -- JDK包含的一些开发工具执行文件\jdk1.6.0\jre\bin\client包含 Java HotSpotTM Client Virtual Machine 要…

java值传递string_关于java:按值传递(StringBuilder与String)

本问题已经有最佳答案&#xff0c;请猛点这里访问。我不明白为什么system.out.println(name)在不受方法的concat函数影响的情况下输出sam&#xff0c;而system.out.println(name)在方法的append方法的结果下输出sam4。为什么StringBuilder受到影响而不是String&#xff1f;通常…

spring源码阅读--aop实现原理分析

aop实现原理简介 首先我们都知道aop的基本原理就是动态代理思想&#xff0c;在设计模式之代理模式中有介绍过这两种动态代理的使用与基本原理&#xff0c;再次不再叙述。 这里分析的是&#xff0c;在spring中是如何基于动态代理的思想实现aop的。为了方便了解接下来的源码分析…

java muki_再次学习 java 类的编译

做JAVA开发的都知道myeclipse&#xff0c; 我们在myeclipse中新建一个类&#xff0c;然后保存&#xff0c; 如何正常的话&#xff0c;那么在项目指定的目录(也就是项目的output目录)就会生成同名的class文件&#xff0c;可是&#xff0c;我们都知道myeclipse中的类的编译的原理…