以注解的方式实现Redis会话token同步
Johsnow场景
公司内部支付管理系统A和第三支付的管理系统B。大部分功能有雷同。现在需要开发一个A / B系统的公共需求。A开发完成之后,B调用A的feign接口。减少开发成本。但是A系统的用户token redis和B系统的用户token redis是分开的。
现在需要在B系统调用A系统的feign接口同时将用户token同步到A的redis。
解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class ThreadLocalUtils {
private static final ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<>();
public static void set(String key, Object value) { Map<String, Object> map = threadLocal.get(); if (map == null) { map = new HashMap<>(); threadLocal.set(map); } map.put(key, value); }
public static Object get(String key) { Map<String, Object> map = threadLocal.get(); if (map != null) { return map.get(key); } return null; }
public static void remove(String key) { Map<String, Object> map = threadLocal.get(); if (map != null) { map.remove(key); } }
public static void clear() { threadLocal.remove(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| @Slf4j @Component public class TokenCachedInterceptor implements HandlerInterceptor { @Autowired private UserContext userContext; @Autowired private RedisTemplate redisTemplate;
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod method = (HandlerMethod) handler; TokenCached annotation = method.getMethodAnnotation(TokenCached.class); if (annotation != null) {
String token = request.getHeader("Tb-Token"); log.info("tb-token:{},sessionUser:{}",token, JSON.toJSONString(userContext.getUser())); if (StringUtils.isNotEmpty(token)) { String redisKey = annotation.prefix() + token; log.info("cacheSessionUser redisKey:{}",redisKey); redisTemplate.opsForValue().set(redisKey, JSON.toJSONString(userContext.getUser()), 30, TimeUnit.SECONDS); } } } return true; }
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod method = (HandlerMethod) handler; TokenCached annotation = method.getMethodAnnotation(TokenCached.class); if (annotation != null) { String token = request.getHeader("Tb-Token"); if (StringUtils.isNotEmpty(token)) { String redisKey = annotation.prefix() + token; log.info("reset expire cacheSessionUser redisKey:{}",redisKey); extendExpiration(redisKey,30, TimeUnit.SECONDS); log.info("重置缓存过期时间:{}",redisKey); } } } } public void extendExpiration(String key, long timeout, TimeUnit timeUnit) { Boolean hasKey = redisTemplate.hasKey(key); if (hasKey != null && hasKey) { redisTemplate.expire(key, timeout, timeUnit); } }
}
|
1 2 3 4 5 6 7 8 9 10 11 12
| @Configuration public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Autowired private TokenCachedInterceptor tokenCachedInterceptor;
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(tokenCachedInterceptor); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Component @Slf4j public class FeignRequestInterceptor implements RequestInterceptor {
@Override public void apply(RequestTemplate template) { if (ObjectUtil.isNotEmpty(RequestContextHolder.getRequestAttributes())){ return; } HttpHeaders headers = (HttpHeaders) ThreadLocalUtils.get("headers"); template.header("Tb-Token", headers.get("Tb-Token")); log.info("异步线程封装Tb-Token:{}",headers.get("Tb-Token")); } }
|
1 2 3 4 5
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface TokenCached { String prefix() default "mjs:sso:login:token:admin_v1:"; }
|
对应controller代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @RestController @RequestMapping("/sendApply") @Slf4j public class SendApplyController { @Resource private SendApplyApi sendApplyApi;
@PostMapping(value = "/v1/add") @TokenCached public ResponseVO add(@RequestBody SendApplyPermissionDto dto) { return sendApplyApi.add(dto); } }
|
[up主专用,视频内嵌代码贴在这]