Hiểu Sâu Về Cơ Chế Quản Lý Phiên Bản HttpSession Trong Web Java

Tổng Quan Về Session

Trong quá trình phát triển ứng dụng web, việc duy trì trạng thái người dùng giữa các lần yêu cầu là một nhu cầu cơ bản. Công nghệ Session đóng vai trò trung tâm trong việc này. Khác với Cookie – vốn lưu trữ thông tin phía trình duyệt của khách hàng – Session hoạt động hoàn toàn trên máy chủ. Điều này giúp dữ liệu nhạy cảm được bảo mật tốt hơn vì không thể truy cập trực tiếp từ client.

Hệ thống Session hoạt động dựa trên sự cộng tác với Cookie. Khi một yêu cầu đầu tiên đến server, một ID duy nhất sẽ được sinh ra để định danh phiên làm việc đó.

1. Quy Trình Hoạt Động Của Session

Lưu đồ xử lý tiêu chuẩn diễn ra như sau:

  1. Một khách hàng gửi yêu cầu HTTP tới Server. Do Session cần Cookie để nhận diện, Server sẽ tạo ra một SessionID độc nhất trong bộ nhớ đệm.
  2. Server truyền SessionID này trở lại cho Client dưới dạng giá trị thuộc tính trong Cookie (Set-Cookie Header).
  3. Khi Client gửi yêu cầu tiếp theo, Cookie sẽ tự động đính kèm SessionID vào header.
  4. Server nhận diện SessionID này để tìm kiếm dữ liệu tương ứng trong bộ nhớ, xác định đây là cùng một phiên làm việc.

2. Đặc Tính Kỹ Thuật

  • Dung lượng lưu trữ: Session không giới hạn kích thước dữ liệu so với Cookie (thường bị giới hạn khoảng 4KB).
  • Type dữ liệu: Có thể lưu trữ bất kỳ đối tượng Java nào (Object), không chỉ chuỗi.
  • Vị trí lưu: Hoàn toàn nằm trong RAM của máy chủ (Tomcat/Jetty).

3. So Sánh Với Cookie

Đặc điểm Cookie Session
Vị trí lưu trữ Clientside (Trình duyệt) Serverside (Máy chủ)
Giới hạn kích thước Có (khoảng 4KB mỗi cookie) Gần như vô hạn (tùy RAM)
An toàn Thấp (có thể bị can thiệp) Cao (người dùng không thấy nội dung)

4. Các Phương Thức Thường Dùng

Class HttpSession cung cấp các hàm quan trọng để quản lý dữ liệu:

// Lấy hoặc tạo mới Session object
HttpSession session = request.getSession(); 

// Lưu dữ liệu vào session
session.setAttribute("keyData", objectValue);

// Đọc dữ liệu đã lưu
Object value = session.getAttribute("keyData");

// Xóa dữ liệu cụ thể
session.removeAttribute("keyData");

// Hủy bỏ toàn bộ session ngay lập tức
session.invalidate(); 

5. Vòng Đời Và Xử Lý Sự Cố

Session có thể bị hủy trong các trường hợp sau:

  1. Người dùng tắt trình duyệt (nếu Cookie chưa set thời gian tồn tại lâu - session cookie).
  2. Thời gian chờ vượt quá cấu hình mặc định (thường là 30 phút).
  3. Dev gọi hàm invalidate().

Về mặt kỹ thuật, nếu Server bị sập đột ngột, dữ liệu Session sẽ mất đi trừ khi có cơ chế Persist (lưu vào ổ đĩa). Tomcat có cơ chế Serialization/Deserialization để lưu Session vào disk khi shutdown (Deadlock/Persistence) và khôi phục khi khởi động lại, đảm bảo dữ liệu không thất thoát ngay lập tức.

6. Ví Dụ Thực Tiễn: Hệ Thống Đăng Nhập Với Mã Xác Thực

Dưới đây là minh họa cách tích hợp Session vào Spring Boot kết hợp JSP để xây dựng chức năng đăng nhập với mã bảo vệ (Captcha).

Bước 1: Controller Tạo Hình Xác Thực

Phần tử này chịu trách nhiệm vẽ mã ngẫu nhiên và lưu nó vào Session.

@Controller
@RequestMapping("/auth")
public class VerificationController {

    @GetMapping("/create-verify-code")
    public void createCode(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        int width = 150;
        int height = 60;
        
        BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = img.getGraphics();
        
        // Vẽ nền xanh lá nhạt
        g.setColor(new Color(200, 255, 200));
        g.fillRect(0, 0, width, height);
        
        String chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
        Random rnd = new Random();
        StringBuilder sb = new StringBuilder();
        
        // Thêm 4 ký tự ngẫu nhiên
        for (int i = 0; i < 4; i++) {
            char c = chars.charAt(rnd.nextInt(chars.length()));
            sb.append(c);
            // Gác màu chữ ngẫu nhiên
            g.setColor(new Color(rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256)));
            g.setFont(new Font("Arial", Font.BOLD, 35));
            g.drawString(String.valueOf(c), 20 + i * 35, 40);
        }
        
        String codeSession = sb.toString();
        req.getSession().setAttribute("captcha_code", codeSession.toUpperCase());
        
        // Vẽ đường nhiễu
        for(int i=0; i<20; i++){
             g.setColor(Color.YELLOW);
             g.drawLine(rnd.nextInt(width), rnd.nextInt(height), rnd.nextInt(width), rnd.nextInt(height));
        }
        
        ImageIO.write(img, "png", resp.getOutputStream());
        g.dispose();
    }
}

Bước 2: Trang Giao Diện Đăng Nhập (login.jsp)

<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page import="javax.servlet.http.HttpServletRequest"%>
<%
    String basePath = request.getContextPath();
%>
<!DOCTYPE html>
<html>
<head>
    <title>Đăng Nhập System</title>
    <style>
        body { font-family: Arial, sans-serif; background-color: #f4f4f4; }
        .form-container { width: 300px; margin: 100px auto; padding: 20px; background: white; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
        input[type="text"], input[type="password"] { width: 80%; padding: 8px; margin: 5px 0; }
        img#captureImg { cursor: pointer; border: 1px solid #ccc; vertical-align: middle; }
    </style>
    <script>
        window.onload = function () {
            document.getElementById('captureImg').onclick = function () {
                this.src = '/auth/create-verify-code?t=' + new Date().getTime();
            };
        }
    </script>
</head>
<body>
    <div class="form-container">
        <h2 align="center">Vào Hệ Thống</h2>
        <form action="/auth/process-login" method="post">
            <p>Tên đăng nhập:<br><input type="text" name="inputUser" /></p>
            <p>Mật khẩu:<br><input type="password" name="inputPass" /></p>
            <p>Mã xác nhận:<br>
                <input type="text" name="inputVerify" />
                <img id="captureImg" src="/auth/create-verify-code" />
            </p>
            <p align="center">
                <button type="submit">Gửi Yêu Cầu</button>
            </p>
            </br>
            <font color="red">${error_msg}</font>
        </form>
    </div>
</body>
</html>

Bước 3: Trang Hiển Thị Sau Khi Thành Công

<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page import="javax.servlet.http.HttpSession"%>
<body>
    <h1 align="center">Chúc Mừng Bạn Đã Đăng Nhập!</b></font></p>
    <a href="/logout">Thoát phiên làm việc</a>
</body>

Bước 4: Xử Lý Logic Xác Thực

Controller chính thức kiểm tra thông tin đầu vào so với dữ liệu đã lưu.

@Controller
@RequestMapping("/auth")
public class AuthController {

    @PostMapping("/process-login")
    public String verifyUser(HttpServletRequest req, Model model) {
        // Thiết lập mã hóa
        req.setCharacterEncoding("UTF-8");
        
        String username = req.getParameter("inputUser");
        String password = req.getParameter("inputPass");
        String inputVerify = req.getParameter("inputVerify");
        
        HttpSession session = req.getSession(false);
        String storedCode = null;
        
        if (session != null) {
            storedCode = (String) session.getAttribute("captcha_code");
        }

        // Kiểm tra xem Session tồn tại và mã code khớp nhau
        if (storedCode == null || !storedCode.equalsIgnoreCase(inputVerify)) {
            model.addAttribute("error_msg", "Mã xác thực không đúng hoặc phiên đã hết hạn.");
            return "login";
        }

        // Giả lập so sánh tài khoản (Thông thường query DB)
        if ("admin".equals(username) && "12345".equals(password)) {
            // Lưu tên người dùng vào session
            session.setAttribute("name_user", username);
            
            // Xóa code sau khi login thành công
            session.removeAttribute("captcha_code");
            return "redirect:/dashboard-view";
        } else {
            model.addAttribute("error_msg", "Tài khoản hoặc mật khẩu sai.");
            return "login";
        }
    }
    
    @GetMapping("/logout")
    public String logout(HttpSession session) {
        session.invalidate();
        return "redirect:/index.jsp";
    }
}

Qua đoạn code trên, chúng ta thấy rõ cách Session lưu trữ trạng thái tạm thời của phiên đăng nhập (mã captcha, tên người dùng) mà không expose dữ liệu lên client.

Thẻ: java-servlet spring-boot http-session JSP session-management

Đăng vào ngày 5 tháng 6 lúc 22:41