因为HTTP Session默认是容器级或内存级的,服务重启、集群部署时Session不共享且无法主动失效,而Redis作为中心化存储可统一管理登录态并支持跨实例操作,这是实现踢人下线的前提。

为什么不能只靠 Session 实现踢人下线
因为 HTTP Session 默认是容器级(如 Tomcat)或内存级的,服务重启、集群部署时 Session 不共享,也无法主动使某个用户的 Session 失效。Redis 作为中心化存储,能统一管理用户登录态,支持跨实例操作——这是实现「踢人」的前提。
关键点在于:每个用户登录后,必须生成唯一且可追溯的 token(比如 JWT 或随机 UUID),并以 user:{uid} 为 key 存入 Redis,值为该 token;同时用 token:{token} 反向映射回用户 ID 和过期时间。这样既能根据用户 ID 主动删 token,也能在每次请求校验时快速定位用户身份。
- 不存明文密码,只存 token + 用户基础信息(如
uid、loginTime) - 务必设置合理的过期时间(如
EXPIRE),避免长期占用内存 - 若用 JWT,注意它不可撤销——必须配合 Redis 黑名单或白名单机制,否则踢人无效
如何设计 Redis Key 结构与更新逻辑
单点登录要求同一用户新登录时,旧 token 必须立即失效。这需要两步原子操作:先查出旧 token,再删掉对应的 token:{oldToken} 和 user:{uid},最后写入新 token。
推荐结构:
user:1001 → "abcde-fghij" # 当前有效 token
token:abcde-fghij → {"uid":1001,"ip":"192.168.1.100","loginTime":1715823400000}
- 登录接口中,先用
GET user:{uid}获取旧 token,存在则调用DEL token:{oldToken} user:{uid} - 再用
SET user:{uid} {newToken} EX 7200和SET token:{newToken} {json} EX 7200写入新凭证 - 用
pipeline或 Lua 脚本保证原子性,避免并发登录时出现双 token 共存 - 不要用
HSET把所有用户塞进一个 Hash,会成为热 key,影响性能和扩容
拦截器里怎么校验 token 并拒绝已踢用户
每次请求进来,从 Header 或 Cookie 中提取 Authorization 或自定义字段(如 X-Auth-Token),然后查 token:{token}。如果返回 nil,说明 token 不存在或已被清除——直接返回 401。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
注意几个易错点:
- 没做空 token 判断,导致 NPE 或 Redis 报
WRONGTYPE错误 - 校验后没刷新过期时间(如用
EXPIRE token:{token} 7200),导致用户频繁重登 - 没把用户 ID 注入到
SecurityContext或RequestContextHolder,后续业务拿不到当前登录人 - 忘记在 logout 接口里同步删掉
user:{uid}和token:{token},造成“登出不干净”
踢人下线接口该怎么写才安全可靠
提供一个管理端接口(如 POST /admin/kickout/{uid}),内部执行:
String oldToken = redisTemplate.opsForValue().get("user:1001");
if (oldToken != null) {
redisTemplate.delete("token:" + oldToken);
redisTemplate.delete("user:1001");
}
但生产环境必须加权限控制和审计日志:
- 仅允许 ADMIN 角色调用,且需二次确认(如传入
operatorId) - 记录操作人、被踢用户、IP、时间到数据库或日志系统,便于追溯
- 前端调用后,应通知客户端主动清空本地 token 缓存,而不是等下次请求失败才感知
- 如果用了 WebSocket,在踢人时最好也关闭对应用户的连接(通过
userId查 Session 并close())
最常被忽略的是:没有处理「用户正在提交表单/上传文件」这类长请求场景。踢人只是删了 token,但正在进行的请求不会中断——得靠前端配合轮询登录态或监听 socket 断连事件来优雅降级。

















