七的博客

Servlet会话管理-Session与Cookie应用

Java

Servlet会话管理-Session与Cookie应用

HTTP 协议一种是无状态的请求。每个 HTTP 请求之间是独立的,服务器不会保留之前的请求信息。

说个比较浅显易懂的例子来理解这个【无状态跟有状态】:

比如你是一个餐厅的老板,你需要定时去一家超市里面采购固定的一些商品。第一天你去采购了一批商品,然后第二天你再去超市里,跟老板说跟昨天那些东西再来一批。

【无状态】: 这个时候超市老板是不知道的,只能要求你重新再说一下买了哪些东西。因为他那里没有记录。

【有状态】:老板直接根据你的身份信息,查询出你上次购买了哪些商品,然后给你再来一批一模一样的商品。

之前我们写过的登录的例子,登录完之后,下一次 HTTP 请求过来,服务器其实还是不知道你有没有登录的。除非是你在请求中携带身份标识,告诉服务器你是谁。

这种时候就引发出会话管理的概念,会话管理可以让多个 HTTP 请求保持用户的登录状态等。

一般会话管理会分 2 种:

  • session

  • cookie

Session

Session 是服务器端的一种机制,可以在多个 HTTP 请求之间维护用户的状态信息。

当客户端第一次访问服务端时,服务端会给当前的客户端创建一个唯一的 Session 对象,并将其与客户端关联。

当服务端给客户端进行应答的时候,会把这个 Session 的唯一标识返回给客户端。 这个唯一标识就是类似于一个 UUID 的字符串,客户端会保存起来。

等下次客户端给服务端发送请求的时候,就会把这个 唯一标识带给服务端。 服务端通过这个唯一标识来分辨出这个 Session 对象属于谁。

Session 里面可以包含任意的数据,比如说用户的登录状态、登录信息等等。 只要你想存的数据都可以往 Session 里面存。

但是这个 Session 数据是保存在服务端的,会占据服务端的内存。

下面演示 Session 的使用,场景还是登录这个例子:

login.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Session 演示登录页面</title>
</head>
<body>
    <h2>填写登录信息</h2>
    <form action="loginServlet" method="post">
        <label for="username">用户名:</label>
        <input type="text" id="username" name="username" required><br>
        <label for="password">密码:</label>
        <input type="password" id="password" name="password" required><br>
        <input type="submit" value="Login">
    </form>
</body>
</html>

LoginServlet.java:

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

       // 这里使用模拟的用户信息验证
        if (validateCredentials(username, password)) {
            // 将登录用户信息存储在 Session 中
            HttpSession session = request.getSession();
            session.setAttribute("username", username);

            response.sendRedirect("welcome.jsp");
        } else {
            response.sendRedirect("login.jsp");
        }
    }

    private boolean validateCredentials(String username, String password) {
        return username.equals("admin") && password.equals("password");
    }
}

welcome.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>欢迎界面</title>
</head>
<body>
    <%
        String username = (String) session.getAttribute("username");
        if (username == null) {
            response.sendRedirect("login.jsp");
        }
    %>
    <h2>欢迎你, <%= username %>!</h2>
    <p>你已经成功登录.</p>
    <a href="logout.jsp">Logout</a>
</body>
</html>

这个例子使用 Session 来存储登录用户的信息。当用户成功登录后 ,把用户名存储在 Session 中。

然后在欢迎页面中,从 Session 中取出用户名,然后显示欢迎消息。

当应用重启后,这个状态还是会丢失。

Cookie

Cookie 是存在浏览器里面的一种机制。

服务端可以在 Java 代码中通过 HttpServletResponse.addCookie() 方法,将 Cookie 数据发送给客户端。浏览器收到后会将 Cookie 保存在浏览器的数据中。

在后续发送请求给服务端的时候,浏览器会自动将这个域名底下相应的 Cookie 信息发送给服务端。 服务端可以通过 HttpServletRequest.getCookies() 获取到 Cookie 中的内容。

Cookie 是存在浏览器中的,所以相对只能存储不敏感的数据,比如一些用户ID、语言等等。

同时 Cookie 是可以被浏览器去删除或者修改的,因此 Cookie 里面的内容不一定可信,这个是需要注意的一点。

下面演示 Cookie 的使用,场景还是登录这个例子:

login.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Cookie 演示登录页面</title>
</head>
<body>
    <h2>填写登录信息</h2>
    <form action="loginServlet" method="post">
        <label for="username">用户名:</label>
        <input type="text" id="username" name="username" required><br>
        <label for="password">密码:</label>
        <input type="password" id="password" name="password" required><br>
        <input type="submit" value="Login">
    </form>
</body>
</html>

LoginServlet.java:

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // 这里使用模拟的用户信息验证
        if (validateCredentials(username, password)) {
            // 将登录用户信息存储在 Cookie 中
            Cookie usernameCookie = new Cookie("username", username);
            usernameCookie.setMaxAge(30 * 60); // 设置 Cookie 的过期时间为30分钟
            response.addCookie(usernameCookie);

            response.sendRedirect("welcome.jsp");
        } else {
            response.sendRedirect("login.jsp");
        }
    }

    private boolean validateCredentials(String username, String password) {
        return username.equals("admin") && password.equals("password");
    }
}

welcome.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>欢迎界面</title>
</head>
<body>
    <%
        String username = null;
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals("username")) {
                    username = cookie.getValue();
                    break;
                }
            }
        }
        if (username == null) {
            response.sendRedirect("login.jsp");
        }
    %>
    <h2>欢迎你, <%= username %>!</h2>
    <p>你已经成功登录.</p>
    <a href="logout.jsp">Logout</a>
</body>
</html>

在这个例子中,LoginServlet 将登录用户信息存储在 Cookie 中,而不是 Session 。

然后创建了一个名为 【username】 的 Cookie,过期时间为 30 分钟。

welcome.jsp 从从 Cookie 中获取用户名,通过遍历 request.getCookies() 返回的 Cookie 数组,查找名为 【username】 的 Cookie,获取这个值然后展示在页面上。

总结

  • Session 存储在服务器端,保存的数据量较大,同时安全性比较高,客户端无法进行篡改。服务端可以随时控制 Session 的声明周期。但是依赖于 Cookie 或 URL 重写,如果用户禁用 Cookie,就需要使用 URL 重写来实现。
  • Cookie 存储在浏览器端,保存的数据量较小,通常会小于 4KB,受限于浏览器的实现。安全性较低,客户端可以进行篡改。存储在客户端,不占用服务器资源。

通常会选择结合使用,将部分数据存储在 Session 中,将一些不敏感的数据存储在 Cookie 中。