Java Web实现“十天内免登录”功能
一、 功能原理
“十天内免登录”的本质是在客户端(浏览器)持久化一个加密的、唯一的身份凭证,从而让服务器在用户关闭浏览器再次打开时,依然能够识别出他的身份。
其核心流程如下图所示:
flowchart TD
A[用户登录] --> B{选择'十天内免登录'?}
B -- 是 --> C[服务器生成Token<br>(用户名+随机数+过期时间)]
B -- 否 --> D[结束]
C --> E[Token与用户关联并存储至数据库]
C --> F[Token加密后写入用户浏览器Cookie]
E --> G[完成登录]
F --> G
H[用户再次访问] --> I[服务器读取Cookie中的Token]
I --> J{Token验证是否有效?<br>(存在、未过期、匹配)}
J -- 无效或不存在 --> K[视为未登录]
J -- 有效 --> L[自动登录<br>并根据Token查询对应用户]
L --> M[为用户创建登录会话Session]
M --> N[完成自动登录]
二、 实现步骤(基于Token方案)
这是一种安全且常用的方案。
数据库准备:
- 在用户表中添加两个字段(或使用单独的表):
remember_token
(VARCHAR):用于存放唯一的令牌字符串。token_validity
(DATETIME/TIMESTAMP):用于存放令牌的过期时间。
- 在用户表中添加两个字段(或使用单独的表):
登录成功后的处理:
- 用户登录时,如果勾选了“十天内免登录”,服务器除了正常创建Session之外,还需要:
- 生成一个唯一且加密安全的随机令牌 (Token)。
- 计算过期时间(如:当前时间 + 10天)。
- 将令牌和过期时间与当前用户关联,并存入数据库。
- 将令牌和用户名(或用户ID)通过Cookie发送给客户端浏览器。
- 用户登录时,如果勾选了“十天内免登录”,服务器除了正常创建Session之外,还需要:
自动登录的过滤与验证:
- 编写一个Filter或Interceptor,在用户访问网站时进行拦截检查。
- 检查顺序:
- 检查请求中是否已有有效的Session?如果有,直接放行。
- 如果没有Session,则检查客户端是否携带了“记住我”的Cookie。
- 如果找到了Cookie,则从中解析出令牌和用户名。
- 根据用户名/用户ID到数据库中查找有效的令牌和过期时间。
- 验证:令牌是否存在、是否匹配、是否未过期。
- 如果验证通过:则自动为用户创建一个新的Session,视为已登录,然后放行。
- 如果验证失败:则删除客户端无效的Cookie,并让其保持未登录状态。
退出登录的处理:
- 用户点击退出时,除了要无效化(invalidate)当前的Session,还需要:
- 删除数据库中对该应用户的令牌记录。
- 删除客户端浏览器中存储“记住我”的Cookie(通过设置一个同名的、值为null的、过期时间为0的Cookie来实现)。
- 用户点击退出时,除了要无效化(invalidate)当前的Session,还需要:
三、 核心代码示例
1. 生成并存储Token(登录Servlet中)
// ... 用户密码验证成功后 ...
if ("on".equals(request.getParameter("rememberMe"))) { // 判断是否勾选
// 1. 生成随机Token
String token = UUID.randomUUID().toString() + ThreadLocalRandom.current().nextLong();
// 更安全的做法是使用 SecureRandom
// 2. 计算过期时间 (10天后)
long validity = System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 10); // 毫秒
Date expiryDate = new Date(validity);
// 3. 将Token和过期时间存入数据库(关联对应用户)
userService.updateRememberToken(currentUser.getId(), token, expiryDate);
// 4. 创建Cookie并发送给客户端
Cookie cookie = new Cookie("rememberMe", token);
cookie.setMaxAge(60 * 60 * 24 * 10); // 秒为单位,10天
cookie.setPath("/"); // 设置为全站有效
cookie.setHttpOnly(true); // 防止XSS攻击获取Cookie,重要!
// cookie.setSecure(true); // 如果使用HTTPS,请启用此选项
response.addCookie(cookie);
}
// 正常创建Session
HttpSession session = request.getSession();
session.setAttribute("user", currentUser);
response.sendRedirect("home.jsp");
2. 自动登录过滤器 (AutoLoginFilter)
@WebFilter("/*") // 拦截所有请求
public class AutoLoginFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
HttpSession session = request.getSession(false); // 获取现有session,不创建新session
// 1. 如果用户session已存在,直接放行
if (session != null && session.getAttribute("user") != null) {
chain.doFilter(request, response);
return;
}
// 2. 检查Remember-Me Cookie
Cookie[] cookies = request.getCookies();
String rememberMeToken = null;
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("rememberMe".equals(cookie.getName())) {
rememberMeToken = cookie.getValue();
break;
}
}
}
// 3. 如果找到了Cookie
if (rememberMeToken != null) {
// 调用Service,通过Token查找用户信息(包含过期时间)
User user = userService.findByRememberToken(rememberMeToken);
// 4. 验证Token和用户是否存在且未过期
if (user != null && user.getTokenValidity().after(new Date())) {
// 验证成功,自动为用户创建Session
session = request.getSession();
session.setAttribute("user", user);
// 可以选择更新Token有效期(滑动过期时间)
} else {
// Token无效或已过期,删除客户端的Cookie
if (user == null) {
// 数据库无此Token,清理无效Cookie
Cookie cookie = new Cookie("rememberMe", null);
cookie.setMaxAge(0);
cookie.setPath("/");
response.addCookie(cookie);
}
}
}
// 5. 继续执行后续的过滤器或请求
chain.doFilter(request, response);
}
}
四、 安全考量与最佳实践
- Token必须是不可预测的:使用
SecureRandom
或UUID
生成高熵(高随机性)的令牌,防止攻击者暴力猜测。 - HttpOnly Cookie:设置Cookie的
HttpOnly
属性为true
,防止JavaScript窃取Cookie(防御XSS攻击)。 - Secure Cookie:如果你的网站使用HTTPS,务必设置Cookie的
Secure
属性为true
,保证Cookie只在加密通道中传输。 - 令牌仅使用一次:更安全的做法是每次自动登录后都生成一个新的令牌替换掉旧的,并更新数据库和Cookie。
- 关联用户ID而非用户名:在Cookie中,可以存储
userId:token
的组合,而不是username:token
,因为ID通常是不可变的,且更难被枚举。 - 重要操作需重新认证:即使用户通过“记住我”功能保持了登录状态,在进行修改密码、支付等敏感操作时,应要求用户重新输入密码进行验证。
- 提供注销选项:一定要提供明显的“退出登录”功能,并正确清理Session和Cookie。
通过以上步骤,你就可以在Java Web项目中实现一个相对安全可靠的“十天内免登录”功能了。