核心实现原理
通过Cookie存储加密令牌实现:
- 用户登录时,后端生成唯一令牌(Token)并关联用户信息
- 令牌同时存入数据库和发送到浏览器Cookie
- 下次访问时,后端验证Cookie中的令牌有效性
- 绝不存储明文密码,仅保存有时效性的令牌
前端实现(JSP示例)
<!-- 登录表单 --> <form action="login" method="post"> <input type="text" name="username" placeholder="用户名"> <input type="password" name="password" placeholder="密码"> <!-- 记住密码复选框 --> <label> <input type="checkbox" name="rememberMe" value="true"> 记住密码 </label> <button type="submit">登录</button> </form>
后端处理(Servlet实现)
// LoginServlet.java protected void doPost(HttpServletRequest request, HttpServletResponse response) { String username = request.getParameter("username"); String password = request.getParameter("password"); boolean rememberMe = "true".equals(request.getParameter("rememberMe")); if (authenticate(username, password)) { // 创建会话 request.getSession().setAttribute("user", username); if (rememberMe) { // 1. 生成唯一令牌 String token = UUID.randomUUID().toString() + System.currentTimeMillis(); // 2. 存储令牌到数据库(需实现) saveTokenToDatabase(username, token); // 3. 创建Cookie(30天有效期) Cookie cookie = new Cookie("rememberToken", token); cookie.setMaxAge(60 * 60 * 24 * 30); // 30天 cookie.setHttpOnly(true); // 防XSS cookie.setPath("/"); if (request.isSecure()) cookie.setSecure(true); // HTTPS启用 response.addCookie(cookie); } // 重定向到主页 response.sendRedirect("home.jsp"); } else { // 认证失败处理 } }
自动登录验证过滤器
// AutoLoginFilter.java public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; // 检查会话是否存在 if (request.getSession().getAttribute("user") == null) { // 检查Cookie Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if ("rememberToken".equals(cookie.getName())) { String token = cookie.getValue(); // 验证令牌有效性(需实现) String username = validateToken(token); if (username != null) { // 创建新会话 request.getSession().setAttribute("user", username); break; } } } } } chain.doFilter(request, response); }
关键安全措施
-
令牌设计
- 使用
UUID + 时间戳
生成唯一令牌 - 数据库存储:
用户名 | 令牌哈希值 | 过期时间
- 示例:
SHA256(token + 盐值)
存储
- 使用
-
Cookie安全
cookie.setHttpOnly(true); // 禁止JS访问 cookie.setSecure(true); // 仅HTTPS传输 cookie.setPath("/"); // 限定路径
-
令牌管理
- 设置有效期(建议7-30天)
- 每次使用后刷新令牌
- 提供用户“退出所有设备”功能
-
防御CSRF
- 添加
SameSite=Strict
属性(Servlet 4.0+)response.setHeader("Set-Cookie", "rememberToken=" + token + "; HttpOnly; Secure; SameSite=Strict; Max-Age=2592000");
- 添加
数据库表设计示例
字段名 | 类型 | 描述 |
---|---|---|
id | BIGINT | 主键 |
username | VARCHAR(50) | 用户名 |
token_hash | CHAR(64) | SHA256加密后的令牌 |
expiry_date | DATETIME | 过期时间 |
最佳实践建议
- 双重验证:敏感操作需重新输入密码
- 令牌黑名单:用户修改密码时使旧令牌失效
- 登录日志:记录设备/IP等异常检测信息
- 定期清理:定时任务删除过期令牌
- 前端提示:在公共电脑上不建议勾选记住密码
安全警告:
永远不要在Cookie中存储明文密码!即使加密也存在被重放攻击的风险,令牌机制的核心思想是使用可撤销的临时凭证替代真实密码。
扩展功能实现
强制重新登录(如修改密码时):
// 使所有令牌失效 public void invalidateTokens(String username) { String sql = "DELETE FROM remember_tokens WHERE username = ?"; // 执行数据库删除 }
多设备管理:
- 在用户设置页显示登录设备列表
- 允许手动撤销特定设备的令牌
通过以上方案,您可以在保证安全性的前提下实现符合用户期待的记住密码功能,关键点在于:令牌代替密码、严格设置Cookie属性、服务器端状态验证,实际部署时建议结合Spring Security等安全框架增强防护。
引用说明:本文实现方案参考OWASP认证安全指南(2025)及RFC 6265 Cookie规范,安全措施部分借鉴了NIST数字身份指南(SP 800-63B)。
原创文章,发布者:酷盾叔,转转请注明出处:https://www.kd.cn/ask/28063.html