这篇文章的话主要是想介绍一下 JSP
中的会话技术,那怎样才是一次会话呢?简单的理解的话,其实可以看做是打开一个浏览器,然后访问自己想要访问的项目,在项目页面中点击多个链接,向后台发送请求,获得响应,最后关闭浏览器。那现在就有一个问题,如何保存每个用户在一次会话中的数据呢?根据我们前面学到的知识,HttpServletRequest
和 ServletContext
对象都可以作为域对象来保存数据,那它们可以用来保存会话中的数据吗?先看 HttpServletRequest
的话,是一个请求对象,很显然保存在这个对象里面的数据在一次请求和响应完成之后就会消失了,而一次会话可能是包含多个请求的,因此不适合,再看 ServletContext
对象的话,是一个应用对应一个对象,其中的数据会被所有用户共享,但是对于每个用户来说的话,会话产生的数据应该是针对个人的,而不应该是全局共享的,因此 ServletContext
对象也不适合。那怎样才能保存每个用户所产生的会话数据呢?这就是我们今天想要介绍的 Cookie
和 Session
了,下面具体进行介绍。
1.Cookie
首先什么是 Cookie
呢?Cookie
其实是在会话过程中,在服务器端创建,保存一定的数据,并响应给浏览器端,第一次浏览器访问服务器是没有 Cookie
的,但是在响应的时候服务器端可能会响应给浏览器 Cookie
,然后浏览器就将 Cookie
保存下来,下一次请求服务器端的时候浏览器就会将 Cookie
自动携带过去,服务器就可以收到 Cookie
中所携带的信息了,当用户关闭浏览器的时候,浏览器中保存的 Cookie
就会全部被销毁了,当然这是在没有给 Cookie
指定生存时长情况下的,指定了生存时长的情况,我们在下面进行说明。
1.1 Cookie的使用
要说 Cookie
的使用的话,肯定就是如何创建、保存和获取了,这里先说保存吧,这里说的保存其实就是在服务器端将 Cookie
响应给浏览器,比如说想保存一个 visitCookie
的 Cookie
到浏览器中,那应该怎么办呢?其实底层的话还是利用的请求和响应模型里面的头信息,因为这里是服务器响应给浏览器,因此肯定是要使用响应对象 HttpServletResponse
的头信息了。
String value = "visitCookie=900000";
response.setHeader("Set-Cookie", value);
这里便是使用的 Set-Cookie
这个响应头,得到的效果就是服务器向浏览器响应了一个名称为 visitCookie
的 Cookie
,并且 Cookie
里面保存的值是 900000
。当然现在 HttpServletResponse
对象提供了更加简单的方法来响应 Cookie
了,那就是
response.addCookie(cookie);
只需要创建一个 Cookie
对象 cookie
,然后使用 HttpServletResponse
对象的 addCookie
方法就可以了,上面这就是保存(响应)Cookie
的方法了。
下面再看如何创建一个 Cookie
,那就需要看 Cookie
这个类的 api
文档了,很明显,Cookie
是有它的构造函数的
public Cookie(String name, String value)
通过上面这个构造函数就可以生成一个 Cookie
对象了,结合上面HttpServletResponse
对象的 addCookie()
方法就可以在服务器端创建一个 Cookie
,然后将它响应到浏览器端了。
最后就是如何获取 Cookie
中的数据了,这里就可以使用 HttpServletRequest
对象了,使用这个对象中的 getCookies()
方法就可以获得这次请求中所携带的全部 Cookie
对象了,当然这时得到的是一个 Cookie
数组,然后就可以根据我们想要找的 Cookie
名称来获取相应的 Cookie
了,从而就能获取到其中的数据了。
Cookie[] cookies = request.getCookies();
上面就是使用 Cookie
时所需要使用的对象以及方法了,掌握上面这些方法就可以很方便的使用 Cookie
了。
1.2 Cookie的分类
这里的分类是从 Cookie
的生存时长来说的,一种就是没有指定生存时长的,即默认级别的;而另一种则是指定了生存时长的,就属于持久级别的了。
1.默认级别
2.持久级别
默认级别的 Cookie
就是没有指定生存时长的 Cookie
,在我们关闭浏览器之后,那么相应的 Cookie
也就被销毁了,很显然,默认级别的 Cookie
是保存在浏览器内存当中的,一旦关闭了浏览器,Cookie
也就销毁了。
持久级别的 Cookie
则是有生存时长的 Cookie
,因为指定了生存时长,所以就算我们关闭了浏览器,相应的 Cookie
也是不会被销毁的,而是要等到其对应的生存时长为 0
之后才会自动销毁,很显然,持久级别的 Cookie
不是保存在浏览器的内存中,而是保存在浏览器所在的硬盘之中。
1.3 Cookie的API文档
其实在上面介绍 Cookie
的使用的时候就已经讲了很多关于 Cookie
的 api
方法,但是这里还是要再说明一下,因为还是有很多细节可以学习的。
public Cookie(String name, String value)
首先便是 Cookie
的构造函数了,我们在创建一个 Cookie
的时候便会使用到这个构造函数了。
public String getName()
public String getValue()
上面的这两个函数便分别是获取 Cookie
的名称和值了。
public void setDomain(String pattern)
上面这个方法则是在我们创建一个 Cookie
之后,指定它所会被使用到的域名,即当浏览器访问哪些域名时会自动带上该 Cookie
,指定域名时可以指定某个单一域名,也可以指定某个域名以及下面的二级域名。
public void setPath(String uri)
这个则是指定 Cookie
会被使用到的项目路径,如果上面方法中的 uri
被赋值为 /
时,则是表示访问 Tomcat
中所有项目时都会自动带上该 Cookie
,而如果 uri
被赋值为 /webCookie
时,则是表示只有在访问 webCookie
这个项目时浏览器才会自动带上该 Cookie
。
public void setMaxAge(int expiry)
这个方法便是设置 Cookie
的最大生存时间了,当我们设置了生存时间之后,那就不是默认级别的 Cookie
了,而应该是持久级别的 Cookie
了,需要注意的是里面 expiry
的单位为秒。其实这个方法也可以用于删除某个 Cookie
,当我们设置某个 Cookie
的最大生存时长为 0
的时候就会使其失效,也就相当于删除了 Cookie
。
1.4 Cookie的细节
下面需要说一些关于 Cookie
的细节信息:
1.一个Cookie只用于标识一种信息,并且带有标识这种信息的名称和值;
2.一个web站点可以给浏览器发送多个Cookie,而一个浏览器也可以保存多个web站点的Cookie;
3.一般来说,一个浏览器可以保存300个Cookie,每个站点的Cookie可以保存20个,而每个Cookie的大小则限制为4kb,不过现在大部分浏览器都有升级,保存Cookie的数量以及一个Cookie能够保存数据的大小都有很大提升;
4.如果创建了一个Cookie,并将它返回给浏览器,则它默认是一个会话级别的Cookie,一旦浏览器关闭了,这个Cookie也会被销毁,如果希望这个Cookie是持久级别的,则需要使用setMaxAge()方法来为Cookie设置最大生存时长;
5.如果需要删除一个Cookie的话,可以直接将它的生存时长设置为0,这样就能删除了,同时需要注意的是,删除时的path必须和创建Cookie时的保持一致。
1.5 Cookie案例
这里想要写一个 Cookie
的案例来加深对 Cookie
的理解,在使用的过程中,也会有慢慢掌握的感觉以及可以发现很多新东西,具体的要求就是:
在第一次访问项目时,在页面中显示欢迎第一次访问,后面访问时则是显示上一次访问时的时间。
这里要用 Cookie
实现的话,很明显,就是要在浏览器向后台发送请求的时候,在服务器端创建一个 Cookie
对象,用来保存这次请求的时间,然后再向浏览器响应该 Cookie
,这个 Cookie
就会保存在浏览器中,然后下次请求时就会自动携带该 Cookie
了,而服务器端也可以根据该 Cookie
的名称来查找,如果没有的话,那就是等一次访问该项目了,应该在页面输出 欢迎第一次访问
,而如果能找到响应名称的 Cookie
的话,那就说明之前已经访问过该项目了,就可以直接从这个 Cookie
中拿到上次访问时的时间了,然后输出到页面中。这里就直接用一个 Servlet
来做了。
具体的代码 CookieServlet
可以如下:
Cookie[] cookies = request.getCookies();
Cookie cookie = CookieUtils.findCookie(cookies, "visitCookie");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType("text/html;charset=UTF-8");
if (cookie == null) {
response.getWriter().print("欢迎第一次访问小窝。。。");
} else {
String dateStr = cookie.getValue();
response.getWriter().print("上一次访问时间为:" + dateStr);
}
Cookie newCookie = new Cookie("visitCookie", new SimpleDateFormat(
"yyyy-MM-dd hh:mm:ss").format(new Date()));
response.addCookie(newCookie);
其中 CookieUtils
的话是一个 Cookie
的工具类,里面有一个 findCookie()
用来在一个 Cookie
数组中寻找指定名称的 Cookie
,代码如下:
public class CookieUtils {
public static Cookie findCookie(Cookie[] cookies, String name) {
if (cookies == null || name == null) {
return null;
}
for (Cookie cookie : cookies) {
if (name.equals(cookie.getName())) {
return cookie;
}
}
return null;
}
}
这样就能实现我们上面所说的需求了,在我们访问项目时,如果是第一次访问,就会在页面中显示 欢迎第一次访问
,而如果不是第一次访问的话,则在页面中显示上一次访问的时间,不过这里稍微有点不如预期的就是,当我们访问了多次之后,在页面中显示的都是上次访问的时间了,但是如果我们关闭浏览器之后再次访问的话,就会发现页面中显示的是 欢迎第一次访问
了,而不是上次访问时的时间了。根据上面已经学到的关于 Cookie
的知识,很快就会发现这是因为 Cookie
的生存时长的原因了,因为我们在服务器端创建 Cookie
时没有指定最长生存时间,因此这是一个会话级别的 Cookie
,当我们关闭浏览器时,这个 Cookie
也就被销毁了,因此再次打开浏览器进行访问的时候就会显示 欢迎第一次访问
了。那么解决这个问题的方法也就是设置最长生存时间了。
Cookie newCookie = new Cookie("visitCookie", new SimpleDateFormat(
"yyyy-MM-dd hh:mm:ss").format(new Date()));
newCookie.setPath("/webCookie");
newCookie.setMaxAge(60);
response.addCookie(newCookie);
上面的代码就是为新创建的 Cookie
设置最大生存时长了,一般在设置生存时长的时候我们也会同时设置路径,这里的 /webCookie
就是我们当前的项目路径了。加上上面的代码之后,就算关闭浏览器之后再次进行访问,也能够拿到上次访问时的时间了,也就可以在页面上进行显示了。完整项目地址为:webCookie
2.Session
首先 Session
是什么呢?学过了上面的 Cookie
之后我们知道,Cookie
是将用户的个人数据保存在浏览器中的对象,而 Session
则是在服务器端创建,并将用户的个人数据保存在服务器端的对象。而且每一个浏览器是独占一个 Session
对象的,当我们想保存数据时,就可以将数据保存到 Session
当中,而需要取数据时,则可以直接从 Session
中获取。
在已经有了 Cookie
之后,为什么还要有 Session
呢?这里就要说一下 Cookie
和 Session
的特点了。
先看 Cookie
的:
1.Cookie保存的数据是有个数和大小的限制的;
2.Cookie的数据是保存在浏览器中的,相对来说不是很安全。
再看 Session
的:
1.Session没有个数和大小的限制;
2.Session中的数据是保存在服务器端的,相对来说更加安全。
2.1 Session的原理
Session
的运行原理是怎样的呢?那就要根据使用 Session
的过程来看了,首先如果我们想在服务器端保存数据,那就需要先使用 HttpServletRequest
对象的 getSession()
方法来获取得到 HttpSession
对象,然后再使用 Session
对象的
public void setAttribute(String name, Object value)
方法将数据保存起来,这样就将数据保存到 Session
当中了。其实在浏览器访问服务器的请求过程中,如果在服务器端使用了 Session
保存该浏览器用户唯一的数据,那么服务器端将会为浏览器用户开辟一个独占的空间来保存相关的数据,这个空间就是 Session
了,并且会有对应唯一的编号,也就是 SessionId
,当服务器响应浏览器的时候就会使用 Cookie
将该 SessionId
同时响应给浏览器,这时候浏览器也就获得了在服务器端保存自己数据的 Session
对象的唯一标识了,在下次再发送请求时,浏览器就会直接携带包含该 SessionId
的 Cookie
了,因此也就可以访问到在服务器端保存的数据了,所以说,Session
运行其实也是依赖于 Cookie
的。
2.2 Session的API文档
在使用 Session
的过程中,其实对 Session
最主要的操作就是获得 Session
对象以及向 Session
对象保存数据和从 Session
中获取数据了,首先看如何获取得到 Session
对象,那就需要使用到 HttpServletRequest
对象了。
public HttpSession getSession()
通过上面这个方法就可以获取得到 Session
对象了,下面再看 Session
对象存取数据的方法。
public void setAttribute(String name, Object value)
使用上面这个方法就可以将数据保存到 Session
当中了。
public Object getAttribute(String name)
上面这个方法则是从 Session
中获取数据了,还有一个方法需要注意的方法就是,从 Session
中移除数据了。
public void removeAttribute(String name)
使用上面这个方法就可以从 Session
中移除指定名称对应的数据了。
2.3 作为域对象的存取数据范围
从上面介绍的 api
方法中就可以看出,Session
对象是可以作为域对象来存取数据的,具体的方法肯定就是 setAttribute()
和 getAttribute()
了,那 Session
作为域对象存取数据的范围是多大呢?因为 Cookie
和 Session
都是会话相关的对象,因此 Session
作为域对象存取数据的范围也就是一次会话范围了,其实也可以从 Session
的原理看出,因为 Session
对象创建完成之后会将 SessionId
使用 Cookie
响应给浏览器,而我们想要访问到服务器端 Session
的数据是需要使用 HttpServletRequest
对象将包含 SessionId
的 Cookie
携带过去的,而一旦关闭浏览器,包含 SessionId
的 Cookie
肯定就会被销毁了,也就访问不到之前在服务器端保存数据的 Session
了,因此可以看出 Session
对象作为域对象保存数据是一次会话范围的。
既然说到了域对象保存数据的作用范围,那就不得不提已经学过的另外两个对象了,那就是 HttpServletRequest
和 ServletContext
对象了。
HttpServletRequest
对象是请求对象,因此它作为域对象保存数据的作用范围也就是一次请求范围了,而 ServletContext
是一个全局对象,因此它的作用范围就是整个应用了。
Session
的作用范围是一次会话,那会话范围的 Session
会在何时被创建以及何时被销毁呢?其实 Session
创建是在服务器端第一次调用 getSession()
方法的时候,那何时会被销毁呢?其实有三种情况:
1.Session过期了,一个Session的过期时间默认为30分钟,当时间到了就会被销毁了,其实这个时间是可以在Tomcat的web.xml配置文件中配置的;
2.非正常关闭服务器,当我们正常关闭服务器时,Session是会被序列化保存下来的;
3.手动调用invalidate()方法也会使Session失效。
2.4 Session案例
这里的案例其实就是一个简单的登录以及退出的功能,当我们验证用户输入验证码是正确的时候,我们就可以使用用户输入的用户名、密码去数据库里面查询是否存在该用户,如果存在就可以将用户对象保存到 Session
当中,这样就表示是登录状态了,登录成功之后就跳转到成功页面,在成功页面中可以检验 Session
中是否包含了用户对象,如果有,则表示是登录状态,如果没有,则表示是未登录状态,就应该提示去登录页登录。当然当用户是登录状态时,也可以进行退出操作,也就是在 Session
中删除相关用户对象了。
下面只列出关键部分的代码了,完整的工程代码以及数据库建表语句都放在 GitHub
上面了。首先看登陆成功时:
HttpSession session = request.getSession();
session.setAttribute("existUser", existUser);
response.sendRedirect("/webLogin/success.jsp");
上面的 existUser
就是使用用户输入的用户名和密码查找到的用户对象。下面再看在登录成功页面 success.jsp
的代码。
<%@page import="domain.User"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录成功页面</title>
</head>
<body>
<%
if (null == request.getSession().getAttribute("existUser")) {
%>
您还未登录!请先去登录:
<a href="/webLogin/login.jsp">登录</a>
<%
} else {
User existUser = (User) request.getSession().getAttribute(
"existUser");
%>
欢迎您!<%=existUser.getNickname()%><a href="/webLogin/LogoutServlet">退出登录</a>
<%
}
%>
</body>
</html>
很显然,在登录页面是可以退出登录的,那就是 /LogoutServlet
要做的事了,也就是删除 Session
中相应的用户对象。
HttpSession session = request.getSession();
session.removeAttribute("existUser");
response.sendRedirect("/webLogin/login.jsp");
核心的代码就是上面这些了,其实最关键的还是要搞清楚整个逻辑的流程。完整项目的地址就是这个了:webLogin
3.总结
其实 Cookie
和 Session
都是为了保存用户独有的数据而存在的,Cookie
是将数据保存在浏览器端,而 Session
则是将数据保存在服务器端,从上面的案例就可以看出,只要掌握了两者的概念以及它们各自的 api
方法,其实还是可以实现很多有意思的小功能的,重点还是要多用吧。