6. 会话跟踪
- 什么是会话? 会话可简单理解为:打开浏览器 -> 访问一些服务器内容 -> 关闭浏览器,整个过程称之为一个会话。
- 会话过程中要解决的问题?
- HTTP 是一种“无状态”协议,每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,服务器要想办法为每个用户保存这些数据:
- 如用户登录场景,当不同的用户登录系统后,我们如何在主页面显示不同的用户名? 购物场景:打开浏览器 -> 浏览商品列表 -> 加入购物车(把商品信息保存下来) -> 关闭浏览器;
- 会话跟踪技术是一种在客户端与服务器间保持HTTP状态的解决方案,管理浏览器客户端和服务器端之间会话过程中产生的会话数据。
- 从开发角度考虑,是使上一次请求所传递的数据能够维持状态到下一次请求,并且辨认出是否相同的客户端所发送出来的。
- 会话跟踪技术的解决方案主要分为以下几种:
- Cookie技术
- Session技术
- URL重写技术
- 隐藏表单域技术
6.1 Cookie
- Cookie技术是一种在客户端保持会话跟踪的解决方案,会话数据保存在客户端浏览器。
- Cookie在用户第一次访问服务器时,由服务器通过响应头的方式发送给客户端浏览器;当用户再次向服务器发送请求时会附带上这些文本信息。
- 在使用Cookie时,要保证客户端浏览器接受Cookie。
javax.servlet.http.Cookie类,用于存储会话数据,存储过程如下
- 1、构造Cookie对象 Cookie(java.lang.String name, java.lang.String value);
- 2、设置cookie
- void setPath(java.lang.String uri):设置cookie的有效访问路径。
- 默认cookie只能由创建它的应用获得,设置cookie.setPath(“/”),则可在同一应用服务器内的所有应用共享访问。
- void setMaxAge(int expiry) : 设置cookie的有效时间
- 该Cookie失效时间,单位秒。如果为正数,则该Cookie在expiry秒之后失效。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1
- void setValue(java.lang.String newValue) :设置cookie的值
- Cookie的值不能设置成非ASSIC字符,如果要使用中文可以通过URLEncoder将其编码,否则会出异常。 Cookie大小和数量的限制,不同的浏览器有所区别,一般每个域名最多可存储50个cookie,Cookie总的的大小限制为4KB。
- void setPath(java.lang.String uri):设置cookie的有效访问路径。
- 3、将Cookie对象响应给客户端浏览器,存储在客户端。 void response.addCookie(Cookie cookie) : 发送cookie
6.1.1 设置和获取cookie
@WebServlet(name = "CookieServlet",urlPatterns = "/getCookie")
public class CookieServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie cookie = new Cookie("token","login-2020年12月10日13:49:56");
response.addCookie(cookie);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
6.1.2 获取cookie
package com.neuedu.session;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 项目: java1-session
* 类名: ${NAME}
* 创建时间: 2020/12/10 13:53
* 描述 : ${dc}
* 作者 : 张金山
* QQ : 314649444
* Site: https://jshand.gitee.io
* http://127.0.0.1:8080/web/showCookie
*/
@WebServlet(name = "ShowCookieServlet",urlPatterns = "/showCookie")
public class ShowCookieServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
StringBuffer cookieInfo = new StringBuffer();
if(cookies != null){
for (Cookie cookie : cookies) {
cookieInfo.append(cookie.getName()+": "+cookie.getValue()+"<br/>");
}
}
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(cookieInfo.toString());
out.flush();
out.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
6.2 Cookie存活时间
Cookie有一定的存活时间,不会在客户端一直保存,默认情况下,Cookie保存在浏览器内存中,在浏览器关闭时失效,这种Cookie也称为会话Cookie,若要使Cookie较长时间的保存在磁盘上,可以通过Cookie对象的setMaxAge()方法设置其存活时间。
Cookie对象可以通过setMaxAge()方法设置其存活时间,时间以秒为单位;
- void setMaxAge(int expiry);
- 时间若为正整数,表示其存活的秒数;
- 时间若为负数,表示其为临时Cookie;
- 时间若为0,表示通知浏览器删除相应的Cookie,删除cookie时,path必须一致,否则不会删除;
- 例如: name.setMaxAge(72460*60);
name.setMaxAge(0)删除Cookie
演示代码
- void setMaxAge(int expiry);
6.3 Cookie的局限性
Cookie的缺点主要集中在其安全性和隐私保护上,主要包括以下几种:
- Cookie可能被禁用,当用户非常注重个人隐私保护时,很可能会禁用浏览器的Cookie功能;
- Cookie可能被删除,因为每个Cookie都是硬盘上的一个文件,因此很有可能被用户删除;
- Cookie的大小和个数受限,不同浏览器有所区别,Cookie保存的数据不能超过4095个字节,50个/每个域名;
- Cookie安全性不够高,所有的Cookie都是以纯文本的形式记录于文件中,因此如果要保存用户名密码等信息时,最好事先经过加密处理。
6.4 Session
- Session是指使用HttpSession对象实现会话跟踪的技术,是一种在服务器端保持会话跟踪的解决方案。
- HttpSession对象是javax.servlet.http.HttpSession接口的实例,也称为会话对象。
- HttpSession对象会在用户第一次访问服务器时由容器创建(注意只有访问JSP、Servlet等程序时才会创建,只访问HTML、IMAGE等静态资源并不会创建),当用户调用其失效方法(invalidate()方法)或超过其最大不活动时间时会失效。在此期间,用户与服务器之间的多次请求都属于同一个会话。
- Session和Cookie的主要区别在于:
- Cookie是把用户的数据写给用户的浏览器。
- Session技术把用户的数据写到用户独占的session中。
- 服务器在创建会话对象时,会为其分配一个唯一的会话标识——SessionId,以“JSESSIONID”的属性名保存在客户端Cookie中,在用户随后的请求中,服务器通过读取Cookie中的JSESSIONID属性值来识别不同的用户,从而实现对每个用户的会话跟踪。
通过下图理解Session的工作原理:
HttpServletRequest接口提供了获取HttpSession对象的方法:
方法 | 描述 |
---|---|
getSession() | 获取与客户端请求关联的当前的有效的Session,若没有Session关联则新建一个 |
getSession(boolean create) | 获取与客户端请求关联的当前的有效的Session,若没有Session关联,当参数为真时,Session被新建,为假时,返回空值 |
6.4.1 创建Session
@WebServlet(name = "CreateServlet",urlPatterns = "/createServlet")
public class CreateSessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
resp.getWriter().write("id:"+session.getId());
}
}
@WebServlet(name = "SessionServlet",urlPatterns = "/session")
public class SessionServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//如果以前没有关联的session会报错
HttpSession session = request.getSession(false);
System.out.println(session.getId());
PrintWriter out = response.getWriter();
out.write(session.getId());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
6.4.2 session的使用
HttpSession接口提供了存取会话域属性和管理会话生命周期的方法
方法 | 描述 |
---|---|
void setAttribute(String key,Object value) | 以key/value的形式将对象保存在HttpSession对象中 |
Object getAttribute(String key) | 通过key获取对象值 |
void removeAttribute(String key) | 从HttpSession对象中删除指定名称key所对应的对象 |
void invalidate() | 设置HttpSession对象失效 |
void setMaxInactiveInterval(int interval) | 设定HttpSession对象的非活动时间(以秒为单位),若超过这个时间,HttpSession对象将会失效 |
int getMaxInactiveInterval() | 获取HttpSession对象的有效非活动时间(以秒为单位) |
String getId() | 获取HttpSession对象标识sessionid |
long getCreationTime() | 获取HttpSession对象产生的时间,单位是毫秒 |
long getLastAccessedTime() | 获取用户最后通过这个HttpSession对象送出请求的时间 |
attribute的使用
@WebServlet(name = "LoginServlet",urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
HttpSession session = request.getSession();
session.setAttribute("username",username);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
获取attribute
@WebServlet(name = "ShowUserInfoServlet", urlPatterns = "/showUser")
public class ShowUserInfoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = (String) request.getSession().getAttribute("username");
PrintWriter out = response.getWriter();
out.write("welcome:"+username);
out.flush();
out.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
删除attribute
@WebServlet(name = "RemoveSessionAttributeServlet",urlPatterns = "/removeAttribute")
public class RemoveSessionAttributeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.removeAttribute("username");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
session销毁
@WebServlet(name = "CreateServlet",urlPatterns = "/createServlet")
public class CreateSessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
resp.getWriter().write("id:"+session.getId());
}
}
当调用完invalite ,在请求createServlet ,sessionid是新的,说明,重新创建session
@WebServlet(name = "InvalidateSessionServlet",urlPatterns = "/invalite")
public class InvalidateSessionServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.invalidate();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
session有效期设置范围
项目中配置、Tomcat默认web.xml中有效期
<session-config>
<session-timeout>1</session-timeout>
</session-config>
Session失效时间 session具有一定声生命周期,如果session超过会话的最大不活动时间,会话自动失效,会话的最大不活动时间指会话超过此时间段不进行任何操作; 设置Session的失效时间
方法1:在工程的web.xml中配置session的生命周期,单位为分钟; <session-config> <session-timeout>15</session-timeout> </session-config>
方法2:在程序硬编码设置 session.setMaxInactiveInterval(30 * 60);//设置单位为秒,设置为-1永不过期;
方法3:在Tomcat安装目录下conf/web.xml中配置(Web容器级别) <session-config> <session-timeout>15</session-timeout> </session-config>
手动销毁Session 可以通过调用invalidate()方法立即清除会话对象及其所有会话域属性,同时响应客户端浏览器清除Cookie中的JSESSIONID,在实际应用中,此方法多用来实现系统的“安全退出”功能。
session对象 setMaxInactiveInterval > 项目(上下文 )> 中间件(Tomcat 30)
@WebServlet(name = "SessionInactiveIntervalServlet", urlPatterns = "/inactive")
public class SessionInactiveIntervalServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.setMaxInactiveInterval(10); //具体某一个会话的 session对象有效
PrintWriter out = response.getWriter();
out.write(session.getId());
out.flush();
out.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
id、CreationTime、 lastAccessedTime
@WebServlet(name = "CreateServlet",urlPatterns = "/createServlet")
public class CreateSessionServlet extends HttpServlet {
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
out.println("id:"+session.getId()+"<br/>");
out.println("lastAccessedTime:"+sdf.format(new Date(session.getLastAccessedTime()))+"<br/>");
out.println("creationTime:"+sdf.format(new Date(session.getCreationTime()))+"<br/>");
out.flush();
out.lose();
}
}
6.5 使用会话技术实现用户登录
6.5.1 LoginServlet
package com.neuedu.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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 java.io.PrintWriter;
/**
* 项目: usermanager
* 类名: ${NAME}
* 创建时间: 2020/12/9 16:07
* 描述 : ${dc}
* 作者 : 张金山
* QQ : 314649444
* Site: https://jshand.gitee.io
* <p>
* http://127.0.0.1:8080/mis/login?type=page
*/
@WebServlet(name = "LoginServlet", urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String type = request.getParameter("type");
//1 展示登录画面 type = page
response.setContentType("text/html;charset=utf-8");
if ("page".equals(type)) {
StringBuffer html = new StringBuffer();
html.append(" <form method='post' action='login'> ");
html.append(" <input type='hidden' name='type' value='validate' /> ");
html.append(" <table border='1' cellpadding='0' cellspacing='0' with='50'> ");
html.append(" <tr> ");
html.append(" <td>用户名</td> ");
html.append(" <td><input type='text' name='username'></td> ");
html.append(" </tr> ");
html.append(" <tr> ");
html.append(" <td>用户名</td> ");
html.append(" <td><input type='password' name='password'></td> ");
html.append(" </tr> ");
html.append(" <tr> ");
html.append(" <td colspan='2' > ");
html.append(" ");
html.append(" <input type='submit' value='登录'></td> ");
html.append(" ");
html.append(" </tr> ");
html.append(" </table> ");
html.append(" </form> ");
PrintWriter out = response.getWriter();
out.write(html.toString());
out.flush();
out.close();
}
//2 验证登录信息 type =validate
else if ("validate".equals(type)) {
String username = request.getParameter("username");
String password = request.getParameter("password");
if (("admin".equals(username) && "admin".equals(password))
|| "root".equals(username) && "root".equals(password)) {
//记录当前登录的人用户名 username
HttpSession session = request.getSession();
session.setAttribute("username", username);
System.out.println("登录成功");
response.sendRedirect(request.getContextPath() + "/user?type=list");
} else {
System.out.println("登录失败");
response.sendRedirect(request.getContextPath() + "/login?type=page");
}
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
6.5.2 需要校验登录状态的Servlet
在响应资源前进行判断(比如UserServlet)
if(!isLogin(request,response)){
//跳转到登录画面
response.sendRedirect(request.getContextPath()+"/login?type=page");
return;
}
private boolean isLogin(HttpServletRequest request, HttpServletResponse response) {
//
HttpSession session = request.getSession();
String username = (String)session.getAttribute("username");
if(username!= null && !"".equals(username)){
return true;
}
return false;
}
6.6 URL重写
https://blog.csdn.net/weixin_40648117/article/details/78844100
