现在我们公司的系统跟第三方合作比较多,所以难以避免的需要调用第三方接口,open-feign这个就非常实用,刚好总结一下,在实际工作中,如何方便地调用第三方接口。
引入Jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>10.10.1</version>
</dependency>
主要是引入openfeign
,然后再用httpclient
进行优化。
application配置
# 启用Httpclient
feign.httpclient.enabled=true
feign.httpclient.disable-ssl-validation=true
# 超时时间设置
feign.client.config.default.connectTimeout=5000
feign.client.config.default.readTimeout=5000
启用Feign
# 启动程序加入注解
@EnableFeignClients
Feign配置
基础配置
import feign.Client;
import feign.Feign;
import feign.Logger;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
//绕过HTTPS
@Bean
public Feign.Builder feignBuilder() {
final Client trustSSLSockets = client();
return Feign.builder().client(trustSSLSockets);
}
public static SSLSocketFactory feignTrustingSSLSocketFactory = null;
@Bean
public Client client() {
if(feignTrustingSSLSocketFactory==null){
try {
feignTrustingSSLSocketFactory = getFeignTrustingSSLSocketFactory();
} catch (Exception e) {
e.printStackTrace();
}
}
return new Client.Default(feignTrustingSSLSocketFactory, new NoopHostnameVerifier());
}
public static SSLSocketFactory getFeignTrustingSSLSocketFactory() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[1];
TrustManager tm = new miTM();
trustAllCerts[0] = tm;
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, null);
return sc.getSocketFactory();
}
static class miTM implements TrustManager, X509TrustManager {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) {
return true;
}
public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) {
return true;
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
throws java.security.cert.CertificateException {
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
throws java.security.cert.CertificateException {
}
}
}
调用第三方接口
@FeignClient(name = "feign-api",
url = "http://localhost:8081/",
configuration = {FeignConfig.class, FeignInterceptor.class})
public interface FeignApi {
@GetMapping("/api/getToken")
String getToken(@RequestParam("appKey") String appKey, @RequestParam("appSecret") String appSecret);
@PostMapping("/api/pushInfo")
String pushInfo(@RequestBody BoardMessage boardMessage);
@PostMapping("/api/pushIndex")
String pushIndex(@RequestBody WarnOrder warnOrder);
}
正常根据上面的代码,就能正常调用第三方接口,但是第三方接口做了token限制,就是token是15分钟失效,调用接口header必须携带token,所以我们还需要对上述代码做一些调整。
Token设置
Token的存储和获取
import java.util.concurrent.ConcurrentHashMap;
public class TokenStore {
public static final String TOKEN = "token";
public static final ConcurrentHashMap<String,String> TOKEN_STORE = new ConcurrentHashMap<>(1);
public static void saveToken(String token){
TOKEN_STORE.put(TOKEN,token);
}
public static String getToken(){
return TOKEN_STORE.get(TOKEN);
}
}
Token 的调用
代码中要考虑到失效刷新和多线程并发问题
@Component
@Slf4j
public class RefreshToken {
@Value("${appKey}")
private String appKey;
@Value("${appSecret}")
private String appSecret;
//60s
public final long MAX_TIME_INTERVAL = 60;
@Resource
private FeignApi feignApi;
public synchronized void makeSureNoExpireToken(){
long currentTime = System.currentTimeMillis()/1000;
if(null == TokenStore.getToken() ||
(getExpireTime(TokenStore.getToken()) - currentTime) <= MAX_TIME_INTERVAL){
// 获取Token
String result = feignApi.getToken(appKey,appSecret);
if(!StringUtils.isEmpty(result)){
JSONObject jsonObject = JSONUtil.parseObj(result);
if(jsonObject.getInt("code")==1){
log.info("正常获取Token");
TokenStore.saveToken(jsonObject.getStr("data"));
}
}
}
}
private long getExpireTime(String token){
JWT jwt = JWTUtil.parseToken(token);
return jwt.getPayloads().getLong("exp");
}
}
Feign中设置token
@Component
public class FeignInterceptor implements RequestInterceptor {
@Resource
private RefreshToken refreshToken;
@Override
public void apply(RequestTemplate template) {
String url = template.url();
log.info("apply url is : {}",url);
if(!url.startsWith("/api/getToken")){
String name = "token";
// 1.先确保Token在有效期内 2.获取token
refreshToken.makeSureNoExpireToken();
String value = TokenStore.getToken();
log.info("current the cloud token is : {}",value);
//同理可以放入Cookie
template.header(name, value);
}
}
}
这样我们就能正常调用第三方接口,而且不需要担心Token刷新问题。