在Feign的请求中添加统一的请求头或请求体
最近有个需求需要对接第三方系统,在调用对方接口时需要在请求头中传入token信息。我想能不能使用Feign来调用第三方的接口,用Feign调用十分简洁,那么在Feign调用的时候如何把token信息放入请求头中呢?
创建Feign对象
在使用Feign调用内部服务时@FeignClient注解的value参数对应相应的服务名即可,在调用第三方接口时只需指定url参数为三方接口的链接即可。值得注意的是url参数也可以直接从yml中获取。
@FeignClient(name = "你的feign名字", url = "${你的yml中配置的三方url}", configuration = Remote56ApiFeignConfig.class)
public interface Remote56ApiFeignClient {
// 获取认证token
@GetMapping(value = "/api/oauth/v1/getToken", headers = {"No-Need-To-Token=true"})
WlApiToken get56ApiTokenByAppId(@RequestParam("appId") String appId, @RequestParam("secret") String secret);
}
创建连接器
在@FeignClient的参数中有个configuration字段可以传入自定义的配置,那么创建你的拦截器配置实现feign包下的RequestInterceptor连接器,重写apply方法来添加你的请求头。
@Slf4j
@Configuration
public class Remote56ApiFeignConfig implements RequestInterceptor {
// 从yml中获取密钥
@Value("${appid}")
private String appId;
// 从yml中获取密钥
@Value("${secret}")
private String secret;
@Resource
@Lazy // 这里需要延迟加载
private Remote56ApiFeignClient remote56ApiFeignClient;
@Resource
private RedisCacheUtil redisCacheUtil;
/**
* 不需要token请求头标识
*/
public static final String NO_NEED_TO_TOKEN = "No-Need-To-Token";
@Override
public void apply(RequestTemplate requestTemplate) {
//获取接口是否不需要加载token 注意在调用获取第三方系统token的接口不需要传token,否则会递归调用进入死循环!!!
boolean noNeedToToken = requestTemplate.headers().containsKey(NO_NEED_TO_TOKEN);
if (!noNeedToToken) {
// 将token存入请求头中
String token = this.getToken(appId, secret);
HashMap<String, Collection<String>> map = new HashMap<>();
map.put("token", Collections.singleton(token));
map.put("appId", Collections.singleton(appId));
requestTemplate.headers(map);
// 打印请求信息
String url = requestTemplate.url();
Map<String, Collection<String>> headers = requestTemplate.headers();
byte[] body = requestTemplate.body();
String method = requestTemplate.method();
log.info("Remote56ApiFeignClient 请求url:{}, headers:{}, body:{}, method:{}", url, JSON.toJSONString(headers), JSON.toJSONString(body), method);
}
}
private String getToken(String appId, String secret) {
log.info("开始获三方系统token,appId:{}, secret:{}", appId, secret);
Object value = redisCacheUtil.get(ToolsCacheKeys.REMOTE_WX_API_TOKEN);
if (value != null) {
WlApiToken wlApiToken = JSON.parseObject(value.toString(), WlApiToken.class);
String token = wlApiToken.getToken();
// 判断是否过期
if (tokenParser(token)) {
redisCacheUtil.del(ToolsCacheKeys.REMOTE_WX_API_TOKEN);
return getToken(appId, secret); // 注意递归调用的退出条件需成立,否则有死循环的风险
}
log.info("获取三方系统token成功:{}", token);
return token;
} else {
WlApiToken wlApiToken = remote56ApiFeignClient.get56ApiTokenByAppId(appId, secret);
if (!Objects.equals(wlApiToken.getCode(), 0)) {
log.error("获取三方系统token失败:{}", wlApiToken.getMsg());
return "test";
}
redisCacheUtil.set(ToolsCacheKeys.REMOTE_WX_API_TOKEN, JSON.toJSONString(wlApiToken), ToolsCacheKeys.REMOTE_WX_API_TOKEN.getExpire());
String token = wlApiToken.getToken();
log.info("获取三方系统token成功:{}", token);
return token;
}
}
/**
* 解析token是否过期
*
* @return true 过期 false 未过期
*/
public boolean tokenParser(String token){
try {
// 使用密钥解析 Token
Claims claims = Jwts.parser()
.setSigningKey(secret.getBytes())
.parseClaimsJws(token)
.getBody();
log.info("解析token成功:{}", claims);
Integer exp = (Integer) claims.get("exp");
// 判断是否过期
if (exp * 1000L < System.currentTimeMillis()) {
log.error("token已过期");
return true;
}
return false;
} catch (Exception e) {
log.error("解析token失败:{}", e.getMessage());
return true;
}
}
}
使用Feign在对接第三方系统虽然和使用http等工具类差不太多,不过我觉得这种更加优雅,🤭