package com.ruoyi.system.service;
|
|
import java.util.Set;
|
import java.util.UUID;
|
import java.util.concurrent.TimeUnit;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import com.ruoyi.common.core.redis.RedisCache;
|
|
/**
|
* 学生端(Miniapp)会话 token 管理 —— Redis UUID token。
|
*
|
* key 与 MiniappController 既有内联实现保持一致:
|
* miniapp:token:{token} = studentId(24h)
|
* 额外维护反向索引 miniapp:tokens:{studentId} = 该生全部 token 集合,
|
* 供"改密/重置密码/强制下线"作废一个学生的所有在线会话。
|
*
|
* @author ruoyi
|
*/
|
@Service
|
public class MiniappTokenService
|
{
|
public static final String TOKEN_PREFIX = "miniapp:token:";
|
public static final String TOKEN_SET_PREFIX = "miniapp:tokens:";
|
/** 学生端登录密码错误计数 key 前缀(管理端重置密码时同步清除) */
|
public static final String PWD_ERR_PREFIX = "miniapp_pwd_err:";
|
/** 自助重置失败计数 key 前缀(管理端修正手机号/重置时同步清除) */
|
public static final String RESET_ERR_PREFIX = "miniapp_reset_err:";
|
|
/** 登录失败锁 key:按 学号+姓名 组合(学号仅班级内唯一,纯学号键会跨班互锁) */
|
public static String pwdErrKey(String studentNo, String name)
|
{
|
return PWD_ERR_PREFIX + studentNo + ":" + name;
|
}
|
|
/** 自助重置失败锁 key(同上组合粒度) */
|
public static String resetErrKey(String studentNo, String name)
|
{
|
return RESET_ERR_PREFIX + studentNo + ":" + name;
|
}
|
private static final int EXPIRE_HOURS = 24;
|
|
@Autowired
|
private RedisCache redisCache;
|
|
/** 生成一个新 token(不落库,配合 setCacheObject 使用) */
|
public String newToken()
|
{
|
return UUID.randomUUID().toString().replace("-", "");
|
}
|
|
/** 把一个已签发的 token 登记进学生反向索引(老链路签发的 token 也应补登记,才能被强制下线覆盖) */
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
public void registerSession(Long studentId, String token)
|
{
|
redisCache.redisTemplate.opsForSet().add(TOKEN_SET_PREFIX + studentId, token);
|
redisCache.redisTemplate.expire(TOKEN_SET_PREFIX + studentId, EXPIRE_HOURS + 1, TimeUnit.HOURS);
|
}
|
|
/** 签发 token + 写会话缓存 + 登记反向索引,一步到位 */
|
public String createSession(Long studentId)
|
{
|
String token = newToken();
|
redisCache.setCacheObject(TOKEN_PREFIX + token, studentId, EXPIRE_HOURS, TimeUnit.HOURS);
|
registerSession(studentId, token);
|
return token;
|
}
|
|
/** 作废某学生全部会话(改密后/重置密码后/管理端强制下线) */
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
public void invalidateAll(Long studentId)
|
{
|
Set<Object> tokens = redisCache.redisTemplate.opsForSet().members(TOKEN_SET_PREFIX + studentId);
|
if (tokens != null)
|
{
|
for (Object t : tokens)
|
{
|
redisCache.deleteObject(TOKEN_PREFIX + t);
|
}
|
}
|
redisCache.deleteObject(TOKEN_SET_PREFIX + studentId);
|
}
|
}
|