Java EE入门(十三)——会话技术(Cookie&Session)基础
Java EE入门(十三)——会话技术(Cookie&Session)基础
iwehdio的博客园:https://www.cnblogs.com/iwehdio/
导图
1、 会话技术
一次会话:浏览器第一次给服务器资源发送请求,会话建立;直到有一方断开为止。一次会话中包含多次请求和响应。
-
会话的功能:在一次会话的范围内的多次请求间,共享资源。
-
方式(数据存储在哪):
- 客户端会话技术:Cookie。
- 服务器端会话技术:Session。
2、Cookie
-
Cookie 是客户端会话技术,将数据保存在客户端。服务器端创建 Cookie 并通过响应发送到客户端,客户端在下一次发送请求时同时发送 Cookie 。
-
使用 Cookie 对象的步骤:
- 服务器端创建 Cookie 对象,绑定数据。
new Cookie(String name, String value)
。
- 服务器端发送 Cookie 对象。
response.addCookie(Cookie cookie)
。
- 服务器端获取 Cookie 对象。
Cookie[] request.getCookies()
。
-
简单实现:
@WebServlet("/Cookie1") public class Cookie1 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //创建Cookie Cookie c = new Cookie("msg", "hello"); //发送Cookie response.addCookie(c); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } } @WebServlet("/Cookie2") public class Cookie2 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取Cookie并遍历 Cookie[] cs = request.getCookies(); if(cs != null){ for(Cookie cc : cs){ String name = cc.getName(); String value = cc.getValue(); System.out.println(name + "_" + value); } } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
- 服务器端创建 Cookie 对象,绑定数据。
-
Cookie 原理分析:
- 第一次响应,服务器端使用响应头中的 set-cookie 头发送 Cookie。
- 第二次请求,浏览器使用请求头中的 cookie 头发送 Cookie。
-
Cookie 的相关问题:
- 一次能否发送多个 Cookie ?
- 一次可以发送多个 Cookie,只需创建多个对象。
- 实质上的同一个响应头 / 请求头下用逗号分隔的多个键值对。
- Cookie 在浏览器中保存多长时间。
- 默认情况下,当浏览器关闭后,Cookie数据被销毁。
- 也可以设置 Cookie 的生命周期,使其持久化存储。
setMaxAge(int seconds)
:传入正数表示将Cookie数据写到硬盘的文件中,数字表示cookie的存活时间;传入负数表示默认状态;传入零表示删除 Cookie 信息。
- Cookie 能否存储中文数据?
- 在 Tomcat8 之后才支持存储中文数据,但仍不支持特殊字符。
- 如果在 Tomcat8 之前,需要将中文数据转码,一般使用 URL 编码。
URLEncoder.encode(String s, String charset)
编码。URLDecoder.decode(String s, String charset)
解码。
- Cookie 的共享:
- 默认情况下,在 Tomcat 服务器中部署的多个项目之间,Cookie 不能共享。
setPath(String path)
:设置 Cookie 的获取范围,默认为当前的虚拟目录。设置为 "/" 表示设置为当前服务器的根目录,可以在同一个服务器下的多个项目间共享。- 不同的 Tomcat 服务器间,通过
setDomain(String path)
设置一级域名相同,那么多个服务器间 Cookie 可以共享。如传入 .baidu.com 那么 tieba.baidu.com 和 news.baidu.com 可以共享。
- 一次能否发送多个 Cookie ?
-
Cookie 的特点 :
- Cookie 存储数据在客户端浏览器,安全性一般。
- 浏览器对单个 Cookie 的大小(一般为4KB)和对同一个域名下的总 Cookie 数量也有限制(一般为20个)。
-
Cookie 的作用:
- 一般用于存储少量的、不太注重安全性的数据。
- 用于在不登录的情况下,完成服务器对客户端的身份识别。
-
案例:记住上一次访问时间。
-
步骤:
- 判断是否有 lasttime 的 Cookie ,根据结果进行响应。
- 更新时间写回 Cookie 。
-
实现:
@WebServlet("/CookieTest") public class CookieTest extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置编码 response.setContentType(("text/html;charset=utf-8")); boolean flag = false; Cookie[] cs = request.getCookies(); if(cs != null && cs.length > 0){ for(Cookie c : cs){ String name = c.getName(); if("lasttime".equals(name)){ //不是首次访问 flag = true; //更新时间 Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); String str_date = sdf.format(date); str_date = URLEncoder.encode(str_date, "utf-8"); c.setValue(str_date); c.setMaxAge(60 * 60 * 24 * 30); response.addCookie(c); //显示上次访问 String value = c.getValue(); value = URLDecoder.decode(value, "utf-8"); response.getWriter().write("上次访问" + value); break; } } } if(cs == null || cs.length == 0 || !flag) { //首次访问 Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); String str_date = sdf.format(date); str_date = URLEncoder.encode(str_date, "utf-8"); Cookie c = new Cookie("lasttime", str_date); c.setMaxAge(60 * 60 * 24 * 30); response.addCookie(c); response.getWriter().write("首次访问"); } System.out.println("3"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
-
2、Session
服务器端会话技术,将数据保存在服务器端对象中。
-
HttpSession 对象的方法:
Object getAttribute(String name)
:获取数据。void setAttribute(String name, Object value)
:设置数据。void removeAttribute(String name)
:移除数据。
-
使用 HttpSession 对象的步骤:
- 在第一个 Servlet 中获取 Session 。
HttpSession session = request.getSession()
。
- 在第一个 Servlet 中存储数据。
session.setAttribute("name", "value")
。
- 在第二个 Servlet 中获取 Session 。
- 在第二个 Servlet 中获取数据。
session.getAttribute("name")
。
- 在第一个 Servlet 中获取 Session 。
-
Session 原理:
- 服务器确保在一次会话范围内,多次获取的 Session 是同一个。
- Session 的实现实际上是依赖于 Cookie 的。
- 第一次获取 Session 时,在内存中创建一个新的 Session 。创建后,服务器会向浏览器响应 Cookie ,内容为 Session 的 ID 即 JSESSIONID 。
- 之后在同一次会话中,浏览器都会携带相同的 JSESSIONID 请求服务器,这样服务器每次都获取同一个 Session 。
-
Session 的相关问题:
-
客户端关闭后,服务器不关闭,两次获取 Session 默认情况下不是同一个。
-
如果要在客户端关闭后, 获取的 Session 也相同,可以手动创建 Cookie 。
-
实现:
//创建 Cookie ,内容为 Session 的 id Cookie c = new Cookie("JSESSIONID", session.getId); //设置 Cookie 的最大存活时间 c.setMaxAge(60*60); //响应 Cookie response.addCookie(c);
-
-
客户端不关闭,服务器关闭,两次获取 Session 默认情况下不是同一个。
- 获取的不是一个对象,但要保证数据不丢失,即对应的 Session id 不变。
- Session 的钝化:在服务器正常关闭直接,将 Session 对象系列化存储到硬盘上。
- Session 的活化:在服务器启动后,将 Session 文件转化为内存中的 Session 对象。
- 直接使用 Tomcat 部署,Session 存储在 项目的 work 目录下,再次启动时自动活化。IDEA 启动项目时会将 work 目录删除,因此无法完成活化。
-
Session 被销毁。
- 服务器被关闭。
- Session 对象调用
invalidate()
方法。 - 默认失效时间30分钟。可以在 web.xml 下的 session-config 标签下配置。
-
-
Session 的特点:
- 用于存储一次会话的多次请求的数据,存在服务器端。
- 可以存储任意类型、任意大小的数据。
- 相较于 Cookie 数据较为安全。
-
案例:用户登录。
-
步骤:
- 编写页面,生成验证码并存储到 Session。
- 提交表单,获取 request 和 Session 参数。
- 先比较验证码,再比较用户名密码。全部正确则重定向到登录成功。有一个不正确则转发到登录页面。
-
实现:
-
login.jsp :
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Login</title> <script> //点击更换验证码 window.onload = function () { document.getElementById("img").onclick = function () { this.src="/Session/Check?time=" + new Date().getDate(); } } </script> </head> <body> <form action="/Session/Session1" method="post"> <table> <tr> <td>登录</td> <td><input type="text" name="username" ></td> </tr> <tr> <td>密码</td> <td><input type="password" name="password" ></td> </tr> <tr> <td>验证码</td> <td><input type="text" name="checkcode" ></td> </tr> <tr> <td colspan="2"><img src="/Session/Check" id="img" ></td> </tr> <tr> <td colspan="2"><input type="submit" value="登录"></td> </tr> </table> </form> <div><%= request.getAttribute("login_error")==null? "" : request.getAttribute("login_error") %></div> <div><%= request.getAttribute("checkcode_error")==null? "" : request.getAttribute("checkcode_error") %></div> </body> </html>
-
Session.java :
@WebServlet("/Session1") public class Session1 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取 request 参数 (多参数应使用 Map 获取) request.setCharacterEncoding("utf-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); String checkcode = request.getParameter("checkcode"); //获取 Session 并设置存储验证 HttpSession session = request.getSession(); String session_checkcode = (String) session.getAttribute("checkcode_session"); session.removeAttribute("checkcode_session"); //先比较验证码(不区分大小写) if(session_checkcode!=null && session_checkcode.equalsIgnoreCase(checkcode)){ //如果验证码正确,比较用户名密码 (具体应使用数据库) if("uadmin".equals(username) && "padmin".equals(password)){ //如果用户名密码正确,重定向到 success.jsp session.setAttribute("username", username); response.sendRedirect(request.getContextPath() + "/success.jsp"); } else { //如果用户名密码错误,转发到 login.jsp request.setAttribute("login_error", "用户名或密码错误"); request.getRequestDispatcher("/login.jsp").forward(request, response); } } else { //如果验证码错误,转发到 login.jsp request.setAttribute("checkcode_error", "验证码错误"); request.getRequestDispatcher("/login.jsp").forward(request, response); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
-
Check.java : 其他同 response 中的验证码,只添加存储到 Session 中。
StringBuilder sb = new StringBuilder(); for (int i = 1; i <= 4; i++) { int index = ran.nextInt(str.length()); //获取字符 char ch = str.charAt(index);//随机字符 sb.append(ch); //写验证码 g.drawString(ch+"",width/5*i,height/2); } String check_code_session = sb.toString(); request.getSession().setAttribute("checkcode_session", check_code_session);
-
success.jsp :
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>success</title> </head> <body> <h1><%= request.getSession().getAttribute("username") %></h1> </body> </html>
-
-
iwehdio的博客园:[https://www.cnblogs.com/iwehdio/](https://www.cnblogs.com/iwehdio/)
来源与结束于否定之否定。