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

Java Web 用戶登入登出完整實現:Session、Cookie、Struts2 與 JSP 整合

     編輯:Java 相關知識

功能需求

  • 用戶輸入帳號密碼登入,驗證成功建立 Session
  • 登入狀態在頁面顯示用戶資訊,提供登出功能
  • 未登入訪問受保護頁面自動跳轉登入頁
  • 支援「記住我」功能 (Cookie 自動登入)

核心類設計

領域模型

public class User {
    private Integer id;
    private String username;
    private String password; // 加密存儲
    private String email;
    private boolean enabled = true;
    // getter/setter/toString
}

Service 介面

public interface UserService {
    User login(String username, String password); // 驗證並返回用戶 (含權限)
    User findById(Integer id);
    void updateLastLoginTime(Integer userId);
}

Struts2 Action

@Controller
@Scope("prototype")
@ParentPackage("shop")
@Namespace("/user")
public class LoginAction extends ActionSupport {
    private String username;
    private String password;
    private boolean rememberMe;
    private String errorMessage;

    @Autowired private UserService userService;

    @Action(value = "login", results = {
        @Result(name = "success", type = "redirectAction", params = {"actionName", "dashboard"}),
        @Result(name = "input", location = "/login.jsp")
    })
    public String login() {
        User user = userService.login(username, password);
        if (user == null) {
            errorMessage = "帳號或密碼錯誤";
            return INPUT;
        }
        if (!user.isEnabled()) {
            errorMessage = "帳號已停用";
            return INPUT;
        }

        // 建立 Session
        HttpSession session = ServletActionContext.getRequest().getSession(true);
        session.setAttribute("LOGIN_USER", user);
        session.setMaxInactiveInterval(30 * 60); // 30 分鐘

        // 記住我:設定 Cookie (加密 Token,實際專案應用 JWT 或持久化 Token)
        if (rememberMe) {
            String token = TokenUtil.generatePersistentToken(user.getId());
            Cookie cookie = new Cookie("REMEMBER_TOKEN", token);
            cookie.setMaxAge(7 * 24 * 60 * 60); // 7 天
            cookie.setPath("/");
            cookie.setHttpOnly(true);
            cookie.setSecure(true); // 僅 HTTPS
            ServletActionContext.getResponse().addCookie(cookie);
        }

        userService.updateLastLoginTime(user.getId());
        return SUCCESS;
    }

    @Action(value = "logout", results = @Result(name = "success", type = "redirectAction", params = {"actionName", "login", "namespace", "/"}))
    public String logout() {
        HttpSession session = ServletActionContext.getRequest().getSession(false);
        if (session != null) session.invalidate();

        // 清除記住我 Cookie
        Cookie cookie = new Cookie("REMEMBER_TOKEN", "");
        cookie.setMaxAge(0);
        cookie.setPath("/");
        ServletActionContext.getResponse().addCookie(cookie);
        return SUCCESS;
    }

    @Action(value = "dashboard", results = @Result(name = "success", location = "/dashboard.jsp"))
    public String dashboard() {
        return SUCCESS;
    }
    // getter/setter
}

登入攔截器 (Struts2 Interceptor)

public class AuthenticationInterceptor extends AbstractInterceptor {
    private static final String[] EXCLUDE_PATTERNS = {"/user/login", "/user/logout", "/static/", "/error/"};

    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        HttpServletRequest request = ServletActionContext.getRequest();
        String uri = request.getRequestURI();

        // 排除靜態資源、登入頁等
        for (String pattern : EXCLUDE_PATTERNS) {
            if (uri.contains(pattern)) return invocation.invoke();
        }

        HttpSession session = request.getSession(false);
        User user = (session != null) ? (User) session.getAttribute("LOGIN_USER") : null;

        // 嘗試 Cookie 自動登入
        if (user == null) {
            user = tryAutoLogin(request);
            if (user != null) {
                request.getSession(true).setAttribute("LOGIN_USER", user);
            }
        }

        if (user == null) {
            // AJAX 請求返回 401,頁面請求重定向
            if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
                HttpServletResponse response = ServletActionContext.getResponse();
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                return null;
            }
            return "loginRedirect"; // struts.xml 定義全域結果
        }

        return invocation.invoke();
    }

    private User tryAutoLogin(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) return null;
        for (Cookie cookie : cookies) {
            if ("REMEMBER_TOKEN".equals(cookie.getName())) {
                String token = cookie.getValue();
                // 驗證 Token (解析用戶 ID、檢查簽名、查詢資料庫、檢查過期)
                return tokenService.validateAndGetUser(token);
            }
        }
        return null;
    }
}

struts.xml 註冊攔截器

<package name="shop" extends="struts-default">
    <interceptors>
        <interceptor name="auth" class="cn.it.shop.interceptor.AuthenticationInterceptor"/>
        <interceptor-stack name="defaultStackWithAuth">
            <interceptor-ref name="defaultStack"/>
            <interceptor-ref name="auth"/>
        </interceptor-stack>
    </interceptors>
    <default-interceptor-ref name="defaultStackWithAuth"/>

    <global-results>
        <result name="loginRedirect" type="redirectAction">
            <param name="actionName">login</param>
            <param name="namespace">/user</param>
        </result>
    </global-results>
    <!-- Action 定義 -->
</package>

JSP 頁面實現

login.jsp

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>用戶登入</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/css/login.css">
</head>
<body>
    <div class="login-container">
        <h2>後台管理系統</h2>
        <s:if test="errorMessage != null">
            <div class="alert alert-danger"><s:property value="errorMessage"/></div>
        </s:if>
        <s:form action="login" namespace="/user" method="post" theme="simple">
            <div class="form-group">
                <label>帳號</label>
                <s:textfield name="username" id="username" cssClass="form-control" required="true"/>
            </div>
            <div class="form-group">
                <label>密碼</label>
                <s:password name="password" id="password" cssClass="form-control" required="true"/>
            </div>
            <div class="form-group checkbox">
                <s:checkbox name="rememberMe" fieldValue="true" label="記住我 (7天)" />
            </div>
            <button type="submit" class="btn btn-primary btn-block">登入</button>
        </s:form>
    </div>
</body>
</html>

dashboard.jsp (顯示登入用戶)

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head><title>儀表板</title></head>
<body>
    <div class="header">
        <span class="welcome">
            歡迎,<c:out value="${sessionScope.LOGIN_USER.username}"/> 
            (<c:out value="${sessionScope.LOGIN_USER.email}"/>)
        </span>
        <a href="<c:url value='/user/logout.action'/>" class="btn btn-link">登出</a>
    </div>
    <div class="content">
        <h3>儀表板內容</h3>
        <!-- 業務模組入口 -->
    </div>
</body>
</html>

安全性最佳實踐

措施實現要點
密碼加密BCrypt/SCrypt/Argon2,不可用 MD5/SHA1,Spring Security PasswordEncoder
防暴力破解登入失敗計數 + IP 封鎖 (Redis 計數器)、驗證碼
Session 安全HttpOnly、Secure、SameSite=Strict、定期換 Session ID
CSRF 防護Struts2 內建 <s:token/>、雙重提交 Cookie
記住我 TokenJWT 或隨機字串 + 資料庫持久化、簽名驗證、過期時間、單設備/多設備策略
HTTPS 強制生產環境全站 HTTPS、HSTS、Secure Cookie

Spring Security 整合建議

若專案規模較大,建議遷移至 Spring Security:

  • 聲明式權限控制 (@PreAuthorize, hasRole)
  • 完整認證授權流程 (表單登入、OAuth2、JWT、RememberMe)
  • Session 管理、並發會話控制、CSRF、CORS
  • 方法級安全、ACL、測試支援
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved