**不要直接在 Cookie 存密碼**。標準流程:
@PostMapping("/login")
public String login(@RequestParam String username,
@RequestParam String password,
@RequestParam(required = false) Boolean rememberMe,
HttpServletResponse response) {
User user = userService.authenticate(username, password);
if (user == null) return "login?error";
// 生成 Token
String token = UUID.randomUUID().toString().replace("-", "");
RememberMeToken rmToken = new RememberMeToken(token, user.getId(),
System.currentTimeMillis() + 30 * 24 * 3600 * 1000); // 30 天
tokenRepository.save(rmToken); // Redis/JPA
if (Boolean.TRUE.equals(rememberMe)) {
ResponseCookie cookie = ResponseCookie.from("REMEMBER_ME", token)
.httpOnly(true).secure(true).path("/").maxAge(30 * 24 * 3600).sameSite("Lax").build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
}
return "redirect:/dashboard";
}
@Component
public class RememberMeFilter extends OncePerRequestFilter {
@Autowired TokenRepository tokenRepo;
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
throws ServletException, IOException {
String token = WebUtils.getCookie(req, "REMEMBER_ME");
if (token != null && SecurityContextHolder.getContext().getAuthentication() == null) {
RememberMeToken rmToken = tokenRepo.findByToken(token);
if (rmToken != null && rmToken.isValid()) {
UserDetails user = userDetailsService.loadUserById(rmToken.getUserId());
Authentication auth = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
// 可選:滑動過期續期
}
}
chain.doFilter(req, res);
}
}
http
.rememberMe()
.tokenRepository(persistentTokenRepository()) // 持久化倉庫
.tokenValiditySeconds(30 * 24 * 3600)
.userDetailsService(userDetailsService)
.key("unique-app-secret-key"); // 簽名密鑰
Spring 官方文檔:Remember-Me Authentication
優秀實戰博客:SpringMVC記住密碼功能