Symfony Remember Me 功能与 Cookie 详解
基本配置
security.yaml 配置
# config/packages/security.yaml
security:
firewalls:
main:
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 7天,单位秒
path: /
domain: ~ # 默认为当前域
secure: false # 是否仅HTTPS
httponly: true
samesite: lax
always_remember_me: false
remember_me_parameter: _remember_me # 表单中的字段名
登录表单实现
{# templates/security/login.html.twig #}
<form method="post">
<input type="email" name="_username" value="{{ last_username }}">
<input type="password" name="_password">
{# Remember me checkbox #}
<label>
<input type="checkbox" name="_remember_me" checked>
记住我
</label>
<button type="submit">登录</button>
</form>
自定义 Remember Me 服务
// src/Security/RememberMeService.php
namespace App\Security;
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
use Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface;
class RememberMeService implements TokenProviderInterface
{
private $connection;
public function __construct($connection)
{
$this->connection = $connection;
}
public function loadTokenBySeries(string $series): PersistentToken
{
// 从数据库加载token
$stmt = $this->connection->prepare(
'SELECT * FROM remember_me_tokens WHERE series = ?'
);
$stmt->execute([$series]);
if (!$data = $stmt->fetch()) {
throw new \Symfony\Component\Security\Core\Exception\TokenNotFoundException();
}
return new PersistentToken(
$data['class'],
$data['username'],
$data['series'],
$data['value'],
new \DateTime($data['last_used'])
);
}
public function createToken(\Symfony\Component\Security\Core\Authentication\RememberMe\PersistentTokenInterface $token): void
{
// 创建新的remember me token
$stmt = $this->connection->prepare(
'INSERT INTO remember_me_tokens (class, username, series, value, last_used) VALUES (?, ?, ?, ?, ?)'
);
$stmt->execute([
$token->getClass(),
$token->getUsername(),
$token->getSeries(),
$token->getValue(),
$token->getLastUsed()->format('Y-m-d H:i:s')
]);
}
public function deleteTokenBySeries(string $series): void
{
// 删除token
$stmt = $this->connection->prepare(
'DELETE FROM remember_me_tokens WHERE series = ?'
);
$stmt->execute([$series]);
}
public function updateToken(string $series, string $tokenValue, \DateTime $lastUsed): void
{
// 更新token
$stmt = $this->connection->prepare(
'UPDATE remember_me_tokens SET value = ?, last_used = ? WHERE series = ?'
);
$stmt->execute([$tokenValue, $lastUsed->format('Y-m-d H:i:s'), $series]);
}
}
Cookie 管理
自定义 Cookie 配置
// src/EventListener/RememberMeCookieListener.php
namespace App\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
use Symfony\Component\Security\Http\Event\LogoutEvent;
class RememberMeCookieListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
LoginSuccessEvent::class => 'onLoginSuccess',
LogoutEvent::class => 'onLogout',
];
}
public function onLoginSuccess(LoginSuccessEvent $event): void
{
// 登录成功后可以添加额外的cookie处理
$request = $event->getRequest();
$response = $event->getResponse();
// 设置自定义cookie
$this->setCustomCookie($response);
}
public function onLogout(LogoutEvent $event): void
{
// 退出时清除所有相关cookie
$response = $event->getResponse();
$this->clearRememberMeCookie($response);
}
private function setCustomCookie($response): void
{
$cookie = new \Symfony\Component\HttpFoundation\Cookie(
'custom_remember_me',
'some_value',
time() + 3600 * 24 * 30, // 30天
'/',
null,
false, // secure
true, // httponly
false,
'lax'
);
$response->headers->setCookie($cookie);
}
private function clearRememberMeCookie($response): void
{
$response->headers->clearCookie('REMEMBERME');
}
}
数据库表结构
-- 持久化token 表
CREATE TABLE remember_me_tokens (
id INT AUTO_INCREMENT PRIMARY KEY,
class VARCHAR(255) NOT NULL,
username VARCHAR(255) NOT NULL,
series VARCHAR(88) NOT NULL UNIQUE,
value VARCHAR(88) NOT NULL,
last_used DATETIME NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_username (username),
INDEX idx_series (series)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
安全检查与验证
// src/Security/RememberMeChecker.php
namespace App\Security;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices;
class RememberMeChecker extends AbstractRememberMeServices
{
protected function processAutoLoginCookie(array $cookieParts, Request $request): ?UserInterface
{
// 验证remember me cookie
if (count($cookieParts) !== 3) {
throw new AuthenticationException('Invalid cookie format');
}
[$series, $tokenValue, $username] = $cookieParts;
// 验证用户是否存在
$user = $this->userProvider->loadUserByIdentifier($username);
if (!$user) {
throw new AuthenticationException('User not found');
}
// 验证token
if (!$this->validateToken($series, $tokenValue)) {
throw new AuthenticationException('Invalid token');
}
return $user;
}
private function validateToken(string $series, string $tokenValue): bool
{
// 实现token验证逻辑
return true; // 简化示例
}
public function loginSuccess(Request $request, Response $response, TokenInterface $token): void
{
// 登录成功后的处理
}
public function logout(Request $request, Response $response, TokenInterface $token): void
{
// 退出时的处理
}
}
最佳实践
-
安全配置:

# 安全建议配置 remember_me: secret: '%kernel.secret%' name: REMEMBERME lifetime: 2592000 # 30天 path: / domain: ~ secure: true # 生产环境建议true httponly: true samesite: strict persistent: true # 使用数据库存储
-
清理过期token:
// src/Command/CleanRememberMeTokensCommand.php namespace App\Command;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface;
class CleanRememberMeTokensCommand extends Command { protected static $defaultName = 'app:clean-remember-me-tokens';
protected function configure(): void
{
$this->setDescription('Clean expired remember me tokens');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
// 清理过期token
$this->entityManager
->createQuery('DELETE FROM App\Entity\RememberMeToken t WHERE t.lastUsed < :date')
->setParameter('date', new \DateTime('-30 days'))
->execute();
$output->writeln('Expired tokens cleaned');
return Command::SUCCESS;
}
3. **定期清理cron job**:
```bash
# crontab
0 3 * * * php /path/to/project/bin/console app:clean-remember-me-tokens
这样配置的Remember Me功能可以安全地让用户保持登录状态,同时提供了完善的cookie管理和安全验证机制。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播