🛡️ 只需一行配置,搞定多端登录、单端登录、互斥登录、七天免登录|Java 登录认证全攻略
作为一名写了 8 年 Java 的后端开发者,我最怕听到产品说:“我们登录要支持多端登录、单端登录、同端互斥登录、PC 和移动端互不影响、七天免登录……能不能搞一下?”
其实,这些登录策略本质上就是 Token 管理策略 + 会话控制。只要设计好认证框架,切换登录策略只需一行配置!
今天这篇文章,我就来手把手教你:
- 各种登录策略的区别
- 如何优雅地实现它们
- 如何通过配置快速切换策略
- 附核心代码实现(高可读 + 注释齐)
🌍 登录策略全景图
| 策略名 | 描述 |
|---|---|
| 多端登录 | 任意设备均可登录,互不影响 |
| 单端登录 | 每次登录都会踢掉上一个设备 |
| 同端互斥登录 | 同一种设备只能登录一个(移动端互踢,PC 和移动端彼此不影响) |
| 七天免登录 | 登录后 7 天内无需再次登录,使用刷新 Token |
🧠 本质理解:登录策略 = Token + Session 管控策略
几乎所有登录策略的区别都在于:
- Token 的生成与存储
- 登录时旧 Token 的处理方式
- 是否启用刷新 Token(七天免登录)
🛠️ 核心设计思路
我们以 JWT + Redis 为基础实现登录认证系统,核心设计如下:
🔑 Token 结构设计(支持多端和互斥)
public class LoginToken {
private String userId; // 用户ID
private String deviceType; // 设备类型 PC / MOBILE / PAD
private String token; // JWT字符串
private long expireAt; // 过期时间
}
Redis 存储结构设计:
// 一个用户在不同设备上的登录状态维护
LOGIN:${userId}:${deviceType} -> token
🧬 登录策略配置
public enum LoginPolicy {
MULTI, // 多端登录
SINGLE_GLOBAL, // 单点登录(全端互斥)
SINGLE_DEVICE, // 同端互斥登录(PC/Mobile 分端互斥)
}
配置中心(application.yml 或数据库配置) :
auth:
login-policy: SINGLE_DEVICE
token-expire-seconds: 3600
refresh-token-expire-seconds: 604800 # 7天
🧩 登录流程核心代码实现
@Service
public class AuthService {
@Value("${auth.login-policy}")
private LoginPolicy loginPolicy;
@Autowired
private RedisTemplate<String, String> redisTemplate;
public String login(String userId, String deviceType) {
String token = JwtUtil.generateToken(userId, deviceType);
// 生成 Redis Key
String redisKey = "LOGIN:" + userId + ":" + deviceType;
switch (loginPolicy) {
case SINGLE_GLOBAL:
// 清除所有设备的登录
cleanAllDeviceSessions(userId);
break;
case SINGLE_DEVICE:
// 清除当前设备上的旧Token
redisTemplate.delete(redisKey);
break;
case MULTI:
// 不清除任何设备
break;
}
// 保存新Token
redisTemplate.opsForValue().set(redisKey, token, Duration.ofHours(1));
return token;
}
private void cleanAllDeviceSessions(String userId) {
Set<String> keys = redisTemplate.keys("LOGIN:" + userId + ":*");
if (keys != null && !keys.isEmpty()) {
redisTemplate.delete(keys);
}
}
}
🔄 七天免登录机制(刷新Token)
✅ 登录时返回两个 Token:
accessToken:有效期短(如1小时)refreshToken:有效期长(如7天)
✅ 当 accessToken 过期,客户端自动用 refreshToken 换新 Token
@PostMapping("/auth/refresh")
public ResponseEntity<?> refreshToken(@RequestBody RefreshRequest request) {
String refreshToken = request.getRefreshToken();
if (!JwtUtil.isValid(refreshToken)) {
return ResponseEntity.status(401).body("Refresh Token失效");
}
String userId = JwtUtil.getUserId(refreshToken);
String deviceType = JwtUtil.getDeviceType(refreshToken);
// 校验是否还在 Redis 中
String redisKey = "LOGIN:" + userId + ":" + deviceType;
String currentToken = redisTemplate.opsForValue().get(redisKey);
if (currentToken == null || !currentToken.equals(refreshToken)) {
return ResponseEntity.status(401).body("会话已过期");
}
// 生成新 accessToken
String newAccessToken = JwtUtil.generateAccessToken(userId, deviceType);
return ResponseEntity.ok(newAccessToken);
}
🔄 切换策略?只需改配置!
auth:
login-policy: SINGLE_GLOBAL
无需改代码,策略立即生效,优雅切换,安全高效。
✅ 总结:登录系统也能模块化配置
| 能力 | 实现方式 |
|---|---|
| 多端登录 | 不清除旧 Token |
| 单端登录 | 清除所有设备旧 Token |
| 同端互斥登录 | 只清当前设备类型 |
| 七天免登录 | access + refreshToken 双 Token 模式 |
📌 最后
这套方案已经在多个中大型项目中稳定运行,包括 SaaS 平台、管理后台、移动端 API 等。它的核心优势在于:
- 开箱即用,策略可配置
- 支持 JWT + Redis,轻量高效
- 逻辑清晰,安全可控
如果你也正好在做登录认证模块,不妨参考这套思路,让你的登录策略“可插拔” ,轻松应对各种产品需求!
作者:一个写了 8 年后端的 Java 工程师,持续关注架构落地、认证安全与系统稳定性。