以注解的方式实现Redis会话token同步

场景

公司内部支付管理系统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) {
// UUID uuid = UUID.randomUUID();
// UUIDRequestContext.setUUID(request, uuid);
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)) {
//UUID uuid = UUIDRequestContext.getUUID();
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;
}
//将当前线程的请求头注入到request中
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:"; // Redis Key 前缀
}

对应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主专用,视频内嵌代码贴在这]