Implementing Multi-Device Token Storage and Validation with Spring Boot, Redis, and Vue
This article explains how to design and implement a token generation, storage, and verification system for both PC and mobile clients in a distributed microservice architecture, using Spring Boot, Redis, custom annotations, AOP aspects, and Vue front‑end integration.
Introduction: In a distributed microservice scenario, token validation and single sign‑on are needed for both PC and mobile clients.
Token storage entity: a LoginToken class with fields PcLoginToken , MobileLoginToken , LoginIP is stored in Redis.
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginToken {
private String PcLoginToken;
private String MobileLoginToken;
private String LoginIP;
}Login service: generates token, stores it in Redis with a 7‑day expiry, and returns it to the client.
@Service
public class loginServiceImpl implements LoginService {
@Autowired UserService userService;
@Autowired RedisUtils redisUtils;
@Override
public R Login(LoginEntity entity) {
// ... (core login logic, token generation, Redis storage)
}
}Enum BizCodeEnum defines response codes; custom exception classes handle login errors such as bad parameters, unknown login type, and token mismatch.
public class BadLoginParamsException extends Exception {
public BadLoginParamsException() {}
public BadLoginParamsException(String message) { super(message); }
}Client‑side storage: a Vue component stores the token in localStorage with an expiration helper to automatically remove expired entries.
Storage.prototype.setExpire = (key, value, expire) => {
let obj = { data: value, time: Date.now(), expire: expire };
localStorage.setItem(key, JSON.stringify(obj));
};
Storage.prototype.getExpire = key => {
let val = localStorage.getItem(key);
if (!val) return null;
val = JSON.parse(val);
if (Date.now() - val.time > val.expire) { localStorage.removeItem(key); return null; }
return val.data;
};Token verification flow: the front‑end sends loginToken , userid , and loginType in request headers; the back‑end validates them via a custom @NeedLogin annotation and an AOP aspect.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedLogin { String value() default ""; } public class VerificationAspect {
@Autowired RedisUtils redisUtils;
@Pointcut("@annotation(com.huterox.common.holeAnnotation.NeedLogin)")
public void verification() {}
@Around("verification()")
public Object verification(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String loginType = request.getHeader("loginType");
String userid = request.getHeader("userid");
String tokenUser = request.getHeader("loginToken");
String tokenKey = RedisTransKey.getTokenKey(userid + ":" + loginType);
if (tokenUser == null || userid == null || loginType == null) throw new BadLoginParamsException();
if (!redisUtils.hasKey(tokenKey)) throw new NotLoginException();
Object o = redisUtils.get(tokenKey);
LoginToken loginToken = JSON.parseObject(o.toString(), LoginToken.class);
if (loginType.equals(LoginType.PcType) && !loginToken.getPcLoginToken().equals(tokenUser))
throw new BadLoginTokenException();
if (loginType.equals(LoginType.MobileType) && !loginToken.getMobileLoginToken().equals(tokenUser))
throw new BadLoginTokenException();
return pjp.proceed();
}
}The controller methods annotated with @NeedLogin trigger the aspect, ensuring that only requests with a valid token stored in Redis can access protected PC or mobile endpoints, completing a secure token‑based authentication mechanism.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.