首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Struts2 整合 Cookie 實現自動登入與驗證碼校驗完整示例

     編輯:Java 相關知識

前言

「記住我」自動登入是 Web 應用的標配功能。本文基於 Struts2 框架,展示如何結合 Cookie、Session、Service 層解密校驗、攔截器思路 實現自動登入,並整合簡單的圖形驗證碼防暴力破解。示例包含完整分層代碼:DAO、Service、Action、JSP、配置文件。

核心流程設計

  1. 登入成功:驗證用戶名/密碼 → 若勾選「記住我」 → 生成 Cookie(存用戶名+加密密碼) → 寫入 Session → 重定向首頁。
  2. 後續訪問:Action 入口方法 autoLogin() 讀取 Cookie → 解密密碼 → 調用 Service 驗證 → 成功則寫 Session 並返回 "success",否則返回 "login"。
  3. 登出:清除 Session、覆蓋 Cookie MaxAge=0。
  4. 驗證碼:單獨 Action 生成圖片流,驗證碼存 Session,登入時校驗。

數據庫配置

# db.properties
dbDriver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:orcl
userName=test
password=password

DAO 層:根據登入名查用戶

package com.struts.dao.impl;

import java.sql.*;
import com.struts.dao.UserDao;
import com.struts.proj.User;
import com.struts.util.BeanConnection;

public class UserDaoImpl implements UserDao {
    private BeanConnection dbconn = new BeanConnection();

    @Override
    public User login(String loginname) {
        String sql = "SELECT * FROM t_scoa_sys_user WHERE f_loginname = ?";
        try (Connection conn = dbconn.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setString(1, loginname);
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    User u = new User();
                    u.setId(rs.getLong(1));
                    u.setF_username(rs.getString(2));
                    u.setF_loginname(rs.getString(3));
                    u.setF_sex(rs.getString(4));
                    u.setF_state(rs.getString(5));
                    u.setF_email(rs.getString(6));
                    u.setF_mobilephone(rs.getString(7));
                    u.setF_secretaryid(rs.getLong(8));
                    u.setF_password(rs.getString(9));  // 已加密存儲
                    u.setF_order(rs.getLong(10));
                    u.setF_note(rs.getString(11));
                    u.setF_infomodifytemplateid(rs.getLong(12));
                    return u;
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
    // save() 略
}

工具類:Cookie 統一管理

package com.struts.util;

import javax.servlet.http.*;
import org.apache.struts2.ServletActionContext;
import com.struts.action.LoginAction;
import com.struts.proj.User;
import com.struts.service.UserService;
import com.struts.service.impl.UserServiceImpl;

public class CookieUtils {
    public static final String USER_COOKIE = "user.cookie";

    // 生成 Cookie:值 = loginname + "," + 解密後密碼(示例為明文,生產必須再加密)
    public Cookie addCookie(User user) {
        Cookie cookie = new Cookie(USER_COOKIE, 
                user.getF_loginname() + "," + DESEDE.decryptIt(user.getF_password()));
        cookie.setMaxAge(60 * 60 * 24 * 365);  // 1 年
        cookie.setPath("/");  // 全路徑有效
        return cookie;
    }

    // 讀取並驗證 Cookie
    public boolean getCookie(HttpServletRequest request, UserService userService) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) return false;

        for (Cookie c : cookies) {
            if (USER_COOKIE.equals(c.getName())) {
                String[] parts = c.getValue().split(",");
                if (parts.length == 2) {
                    String loginname = parts[0];
                    String password = parts[1];  // 已解密
                    User user = userService.login(loginname, password);
                    if (user != null) {
                        request.getSession().setAttribute(LoginAction.USER_SESSION, user);
                        return true;
                    }
                }
            }
        }
        return false;
    }

    // 刪除 Cookie
    public Cookie delCookie(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie c : cookies) {
                if (USER_COOKIE.equals(c.getName())) {
                    c.setValue("");
                    c.setMaxAge(0);
                    c.setPath("/");
                    return c;
                }
            }
        }
        return null;
    }
}

Service 層:驗證邏輯

package com.struts.service.impl;

import com.struts.dao.UserDao;
import com.struts.dao.impl.UserDaoImpl;
import com.struts.proj.User;
import com.struts.service.UserService;
import com.struts.util.DESEDE;

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();

    @Override
    public User login(String loginname, String password) {
        User user = userDao.login(loginname);
        if (user == null) {
            System.out.println("用戶不存在: " + loginname);
            return null;
        }
        // 數據庫存儲的是加密密碼,解密後與輸入明文比對
        if (!DESEDE.decryptIt(user.getF_password()).equals(password)) {
            System.out.println("密碼錯誤: " + loginname);
            return null;
        }
        return user;
    }
}

Struts 配置:struts.xml




    
    

    
        
        
            /success.jsp
            /error.jsp
            /login.jsp
        

        
        
            60
            20
            18
            4
            
                image/jpeg
                inputStream
            
        
    

Action 層:LoginAction 核心邏輯

package com.struts.action;

import javax.servlet.http.*;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.struts.proj.User;
import com.struts.service.UserService;
import com.struts.service.impl.UserServiceImpl;
import com.struts.util.CookieUtils;
import com.struts.util.DESEDE;

public class LoginAction extends ActionSupport {
    private static final long serialVersionUID = 1L;

    public static final String USER_SESSION = "user.session";

    // 表單字段
    private String f_loginname;
    private String f_password;
    private boolean userCookie;  // 記住我
    private String validateCode; // 驗證碼輸入

    private CookieUtils cookieUtils = new CookieUtils();
    private UserService userService = new UserServiceImpl();

    // === 自動登入入口 ===
    public String autoLogin() {
        HttpServletRequest request = ServletActionContext.getRequest();
        if (cookieUtils.getCookie(request, userService)) {
            return "success";
        }
        return "login";
    }

    // === 手動登入 ===
    @Override
    public String execute() {
        HttpSession session = ServletActionContext.getRequest().getSession();

        // 1. 校驗驗證碼
        String code = (String) session.getAttribute("validateCode");
        if (validateCode == null || !validateCode.equalsIgnoreCase(code)) {
            addActionError("驗證碼錯誤");
            return "error";
        }

        // 2. 校驗用戶名密碼
        if (f_loginname != null && !f_loginname.isEmpty() 
                && f_password != null && !f_password.isEmpty()) {
            User user = userService.login(f_loginname, f_password);
            if (user != null) {
                // 3. 記住我 → 寫 Cookie
                if (userCookie) {
                    Cookie cookie = cookieUtils.addCookie(user);
                    ServletActionContext.getResponse().addCookie(cookie);
                }
                // 4. 寫 Session
                session.setAttribute(USER_SESSION, user);
                return "success";
            }
        }
        addActionError("用戶名或密碼錯誤");
        return "login";
    }

    // === 登出 ===
    public String logout() {
        HttpServletRequest request = ServletActionContext.getRequest();
        HttpServletResponse response = ServletActionContext.getResponse();
        HttpSession session = request.getSession(false);
        if (session != null) session.removeAttribute(USER_SESSION);

        Cookie cookie = cookieUtils.delCookie(request);
        if (cookie != null) response.addCookie(cookie);
        return "login";
    }

    // getter/setter 略
}

驗證碼 Action:ValidateCodeAction

package com.struts.action;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Random;
import javax.imageio.ImageIO;
import com.opensymphony.xwork2.ActionSupport;

public class ValidateCodeAction extends ActionSupport {
    private ByteArrayInputStream inputStream;
    private int width = 60, height = 20, fontSize = 18, codeLength = 4;

    @Override
    public String execute() {
        BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = img.getGraphics();
        Random r = new Random();

        // 背景
        g.setColor(getRandomColor(r, 200, 255));
        g.fillRect(0, 0, width, height);

        // 干擾線
        g.setFont(new Font("Times New Roman", Font.PLAIN, fontSize));
        g.setColor(getRandomColor(r, 160, 200));
        for (int i = 0; i < 155; i++) {
            int x = r.nextInt(width), y = r.nextInt(height);
            g.drawLine(x, y, x + r.nextInt(12), y + r.nextInt(12));
        }

        // 驗證碼字符
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < codeLength; i++) {
            String ch = String.valueOf(r.nextInt(10));
            sb.append(ch);
            g.setColor(new Color(20 + r.nextInt(110), 20 + r.nextInt(110), 20 + r.nextInt(110)));
            g.drawString(ch, (width / codeLength) * i + r.nextInt(width / (codeLength * 2)), 
                         r.nextInt(height - fontSize) + fontSize);
        }

        // 存入 Session
        ServletActionContext.getRequest().getSession().setAttribute("validateCode", sb.toString());

        // 輸出圖片流
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            ImageIO.write(img, "JPEG", ImageIO.createImageOutputStream(bos));
            inputStream = new ByteArrayInputStream(bos.toByteArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
        g.dispose();
        return "success";
    }

    private Color getRandomColor(Random r, int fc, int bc) {
        if (fc > 255) fc = 255;
        if (bc > 255) bc = 255;
        int c = fc + r.nextInt(bc - fc);
        return new Color(c, c, c);
    }

    // getter/setter 略
}

JSP 頁面

login.jsp

<%@ taglib prefix="s" uri="/struts-tags"%>

登入

      
    
用戶名:
密碼:
驗證碼:
記住我

success.jsp

<%@ taglib prefix="s" uri="/struts-tags"%>
<%@ page import="com.struts.util.CookieUtils" %>
<%@ page import="org.apache.commons.lang3.StringUtils" %>


    <%
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie c : cookies) {
                if (CookieUtils.USER_COOKIE.equals(c.getName()) && StringUtils.isNotBlank(c.getValue())) {
                    String[] p = c.getValue().split(",");
                    out.println(p[0] + " 歡迎回來!
"); } } } %> 安全退出

安全性增強建議(生產必做)

  1. Cookie 值再加密:示例中存「用戶名,明文密碼」極不安全。應用 AES/DES 加密整個 Cookie 值,或只存隨機 Token,服務端 Redis 映射 Token→用戶信息。
  2. HttpOnly + Secure 標誌:cookie.setHttpOnly(true); cookie.setSecure(true); 防 XSS、強制 HTTPS。
  3. 密碼存儲用 BCrypt/SCrypt/Argon2,而非可逆加密(DESede)。
  4. 驗證碼升級:引入 Google reCAPTCHA、滑塊驗證、設備指紋等更強防禦。
  5. 登入失敗限流:同 IP/用戶 5 分鐘內失敗超 5 次鎖定 15 分鐘。

總結

本示例展示了 Struts2 下Cookie 自動登入 + 驗證碼的完整鏈路:

  • CookieUtils 封裝 Cookie 讀寫刪,業務邏輯清晰。
  • LoginAction.autoLogin() 作為入口攔截,配合 Struts2 攔截器可進一步抽象為全局自動登入攔截器。
  • 驗證碼以 Stream Result 返回圖片流,無需臨時文件。
  • 安全細節(加密、HttpOnly、強雜湊)需根據業務級別補全。

理解此流程,遷移至 Spring MVC / Spring Boot 只需替換 Controller 註解與依賴注入方式,核心邏輯完全復用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved