1:什么是会话
通俗来说就是客户和服务器的一次私密谈话,客户发送请求以后服务器能够识别请求是来自同一个客户,他们是1对1的关系。
了解会话以后我们就要去考虑如何去实现这些问题下面一一进行解析
2:会话的跟踪
2.1:用cookies进行会话跟踪
竟然服务器能别识别不同的用户,但是他是如何识别的呢,这里就说到了SessionId,它是Session的唯一识别,保存在cookies中存放于本地硬盘里面,每次客户请求的时候会把SessionId一起传给服务器,那么服务器就能根据SessionId来识别Session。那么下面我们用代码来演示Session的运行方式
第一步:我们先写一个LoginServlet类
1 public class LoginServlet extends HttpServlet {
2 private static final long serialVersionUID = 1L;
3
4 /**
5 * @see HttpServlet#HttpServlet()
6 */
7 public LoginServlet() {
8 super();
9 }
10
11 /**
12 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
13 */
14 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
15 response.setContentType("text/html;charset=gb2312");
16 HttpSession session=request.getSession();
17 String userName=(String)session.getAttribute("username");
18
19 PrintWriter out =response.getWriter();
20 out.println("<html>");
21 out.println("<meta http-equiv=\"pragma\" content=\"no-cache\">");
22 out.println("<head><title>登录页面</title></head>");
23 out.println("<body>");
24
25 printSessionInfo(out, session);
26 out.println("<p>");
27 out.println("<form action=loginchk method=post>");
28 out.println("<table>");
29 out.println("<tr>");
30 out.println("<td>请输入用户名:</td>");
31 if(userName==null)
32 {
33 out.println("<td><input type=text name=username></td>");
34 }
35 else {
36 out.println("<td><input type=text name=username value="+userName+"></td>");
37 }
38 out.println("</tr>");
39
40 out.println("<tr>");
41 out.println("<td>请输入密码:</td>");
42 out.println("<td><input type=password name=password></td>");
43 out.println("</tr>");
44
45 out.println("<tr>");
46 out.println("<td><input type=reset value=重填></td>");
47 out.println("<td><input type=submit value=登录></td>");
48 out.println("</tr>");
49
50 out.println("</table>");
51 out.println("</form>");
52 out.println("</body>");
53 out.println("</html>");
54 }
55
56 /**
57 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
58 */
59 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
60 doGet(request, response);
61 }
62 /**
63 * 打印与session相关的信息
64 * @param out
65 * @param session
66 */
67 public void printSessionInfo(PrintWriter out,HttpSession session)
68 {
69 out.println("<table>");
70 out.println("<tr>");
71 out.println("<td>会话的状态:</td>");
72 if(session.isNew())
73 {
74 out.println("<td>新的会话</td>");
75 }
76 else {
77 out.println("<td>旧的会话</td>");
78 }
79 out.println("</tr>");
80
81 out.println("<tr>");
82 out.println("<td>会话ID:</td>");
83 out.println("<td>"+session.getId()+"</td>");
84 out.println("</tr>");
85
86 out.println("<tr>");
87 out.println("<td>创建时间:</td>");
88 out.println("<td>"+new Date(session.getCreationTime())+"</td>");
89 out.println("</tr>");
90
91 out.println("<tr>");
92 out.println("<td>上次访问时间:</td>");
93 out.println("<td>"+new Date(session.getLastAccessedTime())+"</td>");
94 out.println("</tr>");
95
96 out.println("<tr>");
97 out.println("<td>最大不活动时间间隔:</td>");
98 out.println("<td>"+session.getMaxInactiveInterval()+"</td>");
99 out.println("</tr>");
100 out.println("</table>");
101 }
LoginServlet
在代码中我们首先获取Session,(如果没有的话Tomcat容器会自动创建一个Session)然后打印出Session的相关信息。然后在渲染出来登录界面
第二步:我们在写一个LoginChkServlet类
1 public class LoginChkServlet extends HttpServlet {
2 private static final long serialVersionUID = 1L;
3
4 /**
5 * @see HttpServlet#HttpServlet()
6 */
7 public LoginChkServlet() {
8 super();
9 }
10
11 /**
12 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
13 */
14 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
15 request.setCharacterEncoding("gb2312");
16 String userName=request.getParameter("username");
17 String passWord=request.getParameter("password");
18
19 if(userName==null||userName.equals("")||passWord==null||passWord.equals(""))
20 {
21 response.sendRedirect("login");
22 return;
23 }
24 else
25 {
26 HttpSession session=request.getSession();
27 session.setAttribute("username", userName);
28 response.sendRedirect("welcome");
29 }
30 }
31
32 /**
33 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
34 */
35 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
36 // TODO Auto-generated method stub
37 doGet(request, response);
38 }
LoginChkServlet
这个Servlet类主要是验证登录的用户信息,如果成功就把用户名写入Session中
第三步:写一个登录成功以后指向的WelcomeServlet类
1 public class WelcomeServlet extends HttpServlet {
2 private static final long serialVersionUID = 1L;
3
4 /**
5 * @see HttpServlet#HttpServlet()
6 */
7 public WelcomeServlet() {
8 super();
9 // TODO Auto-generated constructor stub
10 }
11
12 /**
13 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
14 */
15 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
16 HttpSession session=request.getSession();
17 String userName=(String)session.getAttribute("username");
18
19 if(userName==null)
20 {
21 response.sendRedirect("login");
22 }
23 else
24 {
25 response.setContentType("text/html;charset=gb2312");
26 PrintWriter out=response.getWriter();
27 out.println("<html><head><title>欢迎页面</title></head><body>");
28 LoginServlet loginServlet=new LoginServlet();
29 loginServlet.printSessionInfo(out, session);
30
31 out.println("<p>");
32 out.println("欢迎你,"+userName+"<p>");
33 out.println("<a href=login>重新登录</a>");
34 out.println("<a href=logout>注销</a>");
35 out.println("</body></html>");
36 out.close();
37 }
38 }
39
40 /**
41 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
42 */
43 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
44 // TODO Auto-generated method stub
45 doGet(request, response);
46 }
WelcomeServlet
这个类是登录成功以后要显示的界面
第四步:在写一个退出类LogOutServlet类
1 public class LogOutServlet extends HttpServlet {
2 private static final long serialVersionUID = 1L;
3
4 /**
5 * @see HttpServlet#HttpServlet()
6 */
7 public LogOutServlet() {
8 super();
9 // TODO Auto-generated constructor stub
10 }
11
12 /**
13 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
14 */
15 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
16 response.setContentType("text/html;charset=gb2312");
17 HttpSession session=request.getSession();
18 session.invalidate();
19
20 PrintWriter out=response.getWriter();
21 out.println("<html><head><title>退出登录</title></head><body>");
22 out.println("已退出登录<br>");
23 out.println("<a href=login>重新登录</a>");
24 out.println("</body></html>");
25 out.close();
26 }
27 /**
28 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
29 */
30 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
31 // TODO Auto-generated method stub
32 doGet(request, response);
33 }
LogOutServlet
这个类是清除Session返回登录页面。
然后我们在配置一下web.xml
1 <web-app>
2 <display-name>Archetype Created Web Application</display-name>
3 <servlet>
4 <servlet-name>LoginServlet</servlet-name>
5 <display-name>LoginServlet</display-name>
6 <description></description>
7 <servlet-class>com.lp.servlet.LoginServlet</servlet-class>
8 </servlet>
9 <servlet>
10 <servlet-name>LoginChkServlet</servlet-name>
11 <display-name>LoginChkServlet</display-name>
12 <description></description>
13 <servlet-class>com.lp.servlet.LoginChkServlet</servlet-class>
14 </servlet>
15 <servlet>
16 <servlet-name>WelcomeServlet</servlet-name>
17 <display-name>WelcomeServlet</display-name>
18 <description></description>
19 <servlet-class>com.lp.servlet.WelcomeServlet</servlet-class>
20 </servlet>
21 <servlet>
22 <servlet-name>LogOut</servlet-name>
23 <display-name>LogOut</display-name>
24 <description></description>
25 <servlet-class>com.lp.servlet.LogOutServlet</servlet-class>
26 </servlet>
27 <servlet-mapping>
28 <servlet-name>LoginServlet</servlet-name>
29 <url-pattern>/login</url-pattern>
30 </servlet-mapping>
31 <servlet-mapping>
32 <servlet-name>LoginChkServlet</servlet-name>
33 <url-pattern>/loginchk</url-pattern>
34 </servlet-mapping>
35 <servlet-mapping>
36 <servlet-name>WelcomeServlet</servlet-name>
37 <url-pattern>/welcome</url-pattern>
38 </servlet-mapping>
39 <servlet-mapping>
40 <servlet-name>LogOut</servlet-name>
41 <url-pattern>/logout</url-pattern>
42 </servlet-mapping>
43 <session-config>
44 <session-timeout>10</session-timeout>
45 </session-config>
46 </web-app>
web.xml
其中<session-config>是设置Session的过期时间。ok现在整个项目写好了,我们运行一下看看结果
注解:由于是第一次访问所以服务器会首次创建会话所以这是一个新的会话,最大不活动时间指的就是Session失效的时间,由于我们在配置文件定义的是10分钟,所以默认就是60*10=600s(输出的单位是秒)
如果我用F5刷新我们看下结果
我们可以看出已经是旧的会话了,而且他们的SessionId是相同的。这是说明这2次是同一个会话,然后我们输入用户名和密码进行登录
我们通过SessionId可以看出这依然是同一个会话,然后我们在点击重新登录按钮输入用户名和密码如下图
我们发现SessionId依然相同,这说明了一个问题就是同一个浏览器并不支持2个用户同时登录,第二个登录的用户会把第一个用户信息进行覆盖(这也是Session覆盖问题)
但是有人会疑问如果用另一种浏览器登录会不会还是同一个会话呢,那么我们在IE浏览器输入http://localhost:8080/session-test/login如下图
是一个新的会话,这就说明不同浏览器会有不同会话的。那么有人就说如果我们禁用Cookies了是不是就不可以登录了呢我们来看看结果。
登录不上了,这也恰恰说明SessionId需要Cookies,刚刚禁止Cookies以后我自己都登录不了博客园,(⊙﹏⊙)。那么怎么办呢 就有另一种方法Url重写机制。
2:利用URL重写机制来跟踪会话
指的是如果客户端不支持Cookies的时候,可以使用URL重写机制来跟踪会话。URL重写机制就是在URL中附加客户的标识SessionId,Servlet容器解释URL的时候取出SessionId,在Servlet容器规范中这个参数的名字必须是jsessionid,完整的例子如http://localhost:8080/session-test/login;jsessionid=1。这样一来服务器将SessionId作为Url一部分发送给客户端,客户端在请求URL的时候再把SessionId传回来这样就会达到会话跟踪。
我们把上面代码中类似于
1:response.sendRedirect("login")修改为response.sendRedirect(response.encodeRedirectURL("login"));
2:action=loginchk修改为action="+response.encodeURL("loginchk")
然后再次运行就会发现可以登录了如下图
登录成功以后我们从浏览器来看一下jsessionid,会发现和上面的sessionId一模一样
3:Session的其他知识
3.1:当关闭浏览器以后,Session如果没有达到失效的时间是没有消除的,登录以后依然会发现是一个旧的Session。
3.2:用户会话跟踪的Cookies名字必须是jsessionid,通常报错在浏览器内存中,在浏览器内存中的cookies是不能被不同浏览器进程所共享。
3.3:如果想用另一种方式了解Session,我的另一个博客用.net写的也讲解了Session