Backend Development 17 min read

Token Storage and Validation in Distributed Microservices Using Redis and Custom Annotations

This article explains how to generate, store, and validate login tokens across PC and mobile clients in a distributed microservice architecture, covering Redis storage, custom token entities, Spring service implementation, exception handling, client‑side storage utilities, and a custom annotation with an AOP aspect for request verification.

Top Architect
Top Architect
Top Architect
Token Storage and Validation in Distributed Microservices Using Redis and Custom Annotations

In a distributed microservice scenario, token validation and single sign‑on are implemented by generating a token, storing it in Redis, and verifying it on each request.

Token storage entity

@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginToken {
    // Token stored in Redis
    private String PcLoginToken;
    private String MobileLoginToken;
    private String LoginIP;
}

Login service implementation

@Service
public class loginServiceImpl implements LoginService {
    @Autowired UserService userService;
    @Autowired RedisUtils redisUtils;
    // rate‑limit protection
    @Override
    public R Login(LoginEntity entity) {
        String username = entity.getUsername();
        String password = entity.getPassword();
        password = password.replaceAll(" ", "");
        if (redisUtils.hasKey(RedisTransKey.getLoginKey(username))) {
            return R.error(BizCodeEnum.OVER_REQUESTS.getCode(), BizCodeEnum.OVER_REQUESTS.getMsg());
        }
        redisUtils.set(RedisTransKey.setLoginKey(username), 1, 20);
        UserEntity user = userService.getOne(new QueryWrapper
().eq("username", username));
        if (user != null) {
            if (SecurityUtils.matchesPassword(password, user.getPassword())) {
                String token = JwtTokenUtil.generateToken(user);
                String ipAddr = GetIPAddrUtils.GetIPAddr();
                if (entity.getType().equals(LoginType.PcType)) {
                    LoginToken loginToken = new LoginToken(token, null, ipAddr);
                    redisUtils.set(RedisTransKey.setTokenKey(user.getUserid() + ":" + LoginType.PcType), loginToken, 7, TimeUnit.DAYS);
                    return Objects.requireNonNull(R.ok(BizCodeEnum.SUCCESSFUL.getMsg()).put(LoginType.PcLoginToken, token)).put("userid", user.getUserid());
                } else if (entity.getType().equals(LoginType.MobileType)) {
                    LoginToken loginToken = new LoginToken(null, token, ipAddr);
                    redisUtils.set(RedisTransKey.setTokenKey(user.getUserid() + ":" + LoginType.MobileType), loginToken, 7, TimeUnit.DAYS);
                    return Objects.requireNonNull(R.ok(BizCodeEnum.SUCCESSFUL.getMsg()).put(LoginType.PcLoginToken, token)).put("userid", user.getUserid());
                } else {
                    return R.error(BizCodeEnum.NUNKNOW_LGINTYPE.getCode(), BizCodeEnum.NUNKNOW_LGINTYPE.getMsg());
                }
            } else {
                return R.error(BizCodeEnum.BAD_PUTDATA.getCode(), BizCodeEnum.BAD_PUTDATA.getMsg());
            }
        } else {
            return R.error(BizCodeEnum.NO_SUCHUSER.getCode(), BizCodeEnum.NO_SUCHUSER.getMsg());
        }
    }
}

Enum and exception definitions

public enum BizCodeEnum {
    UNKNOW_EXCEPTION(10000, "系统未知异常"),
    VAILD_EXCEPTION(10001, "参数格式校验失败"),
    ...
    SUCCESSFUL(200, "successful");
    private int code;
    private String msg;
    BizCodeEnum(int code, String msg) { this.code = code; this.msg = msg; }
    public int getCode() { return code; }
    public String getMsg() { return msg; }
}
public class BadLoginParamsException extends Exception { public BadLoginParamsException() {} public BadLoginParamsException(String message) { super(message); } }
public class BadLoginTokenException extends Exception { public BadLoginTokenException() {} public BadLoginTokenException(String message) { super(message); } }
public class NotLoginException extends Exception { public NotLoginException() {} public NotLoginException(String message) { super(message); } }

Client‑side storage utilities (localStorage extension)

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;
};

Front‑end login component (Vue + Element UI)

<template>
  <div>
    <el-form :model="formLogin" :rules="rules" ref="ruleForm" label-width="0px">
      ... (username, password, captcha fields) ...
    </el-form>
  </div>
</template>
<script>
import SIdentify from "../../components/SIdentify/SIdentify";
export default {
  name: "loginbyUserName",
  components: { SIdentify },
  data() { return { formLogin: { username: "", password: "", code: "" }, identifyCodes: '1234567890abcdefjhijklinopqrsduvwxyz', identifyCode: '' , rules: { username: [{ required: true, message: "请输入用户名", trigger: "blur" }], password: [{ required: true, message: "请输入密码(区分大小写)", trigger: "blur" }], code: [{ required: true, message: "请输入验证码", trigger: "blur" }] } }; },
  mounted() { this.identifyCode = ''; this.makeCode(this.identifyCodes, 4); },
  methods: {
    refreshCode() { this.identifyCode = ''; this.makeCode(this.identifyCodes, 4); },
    makeCode(o, l) { for (let i = 0; i < l; i++) { this.identifyCode += this.identifyCodes[this.randomNum(0, this.identifyCodes.length)]; } },
    randomNum(min, max) { return Math.floor(Math.random() * (max - min) + min); },
    submitForm() { if (this.formLogin.code.toLowerCase() !== this.identifyCode.toLowerCase()) { this.$message.error('请填写正确验证码'); this.refreshCode(); } else { this.axios({ url: "/user/user/login", method: 'post', data: { username: this.formLogin.username, password: this.formLogin.password, type: "PcType" } }).then(res => { res = res.data; if (res.code === 10001) { alert('请将对应信息填写完整!'); } else if (res.code === 0) { alert('登录成功'); localStorage.setExpire("LoginToken", res.PcLoginToken, this.OverTime); localStorage.setExpire("userid", res.userid, this.OverTime); this.$router.push({ path: '/userinfo', query: { userid: res.userid } }); } else { alert(res.msg); } }); } }
  }
};
</script>

Custom annotation for login verification

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedLogin { String value() default ""; }

AOP aspect that checks token validity

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)) {
            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();
            }
        } else {
            throw new NotLoginException();
        }
        return pjp.proceed();
    }
}

Usage example in a controller

@RestController
@RequestMapping("/user/user/space")
public class SpaceController {
    @NeedLogin
    @GetMapping("/isLogin")
    public R isLogin(@RequestHeader("userid") String userid, @RequestHeader("loginToken") String token) {
        // business logic after token verification
        return R.ok();
    }
}

The article also contains promotional sections for a community, giveaways, and related links, but the core technical content focuses on token management, Redis persistence, client‑side handling, and AOP‑based authentication in a Java Spring microservice environment.

JavaMicroservicesRedisSpringAuthenticationtokenaspect-oriented
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.