OkHttp全局拦截器设置token超时重新获取
Feign客户端请求远程服务接口时,需要携带token进行认证(详见《微服务迁移记(六):集成jwt保护微服务接口安全》),token有超时时间设置,当超时后,需要重新刷新token。如果每个接口都去判断,那就费事了,最好的办法是在拦截器里做。我这里使用的是OkHttp,新增一个OkHttpInterceptor的拦截器:
@Slf4j public class OkHttpInterceptor implements HandlerInterceptor,Interceptor { @Autowired private ApiInitService apiInitService; @Autowired private RedisUtil redisUtil; @Value("${app_id}") String app_id; @Value("${app_secret}") String app_secret; @Override public Response intercept(Chain chain) throws IOException { log.info("进入okhttp拦截器"); Request request = chain.request(); try { Response response = chain.proceed(request); ResponseBody responseBody = response.body(); BufferedSource source = responseBody.source(); source.request(Long.MAX_VALUE); Buffer buffer = source.getBuffer(); MediaType mediaType = responseBody.contentType(); if(isPlaintext(buffer)){ Charset charset = Charset.forName("UTF-8"); String result = buffer.clone().readString(mediaType.charset(charset)); log.info("result:"+result); //如果token超时或不存在,则重新获取 ResponseData responseData = JSONObject.parseObject(result,ResponseData.class); if(responseData.getCode() == CodeEnum.UNKNOWNTOKEN.getCode()){ //重新获取token String tokenData = this.apiInitService.getToken(app_id,app_secret); if(StringUtils.isNotEmpty(tokenData)){ //重新将token存到redis中 redisUtil.set("Authorization",tokenData); } } } return response; }catch (Exception e){ throw e; } } /** * Returns true if the body in question probably contains human readable text. Uses a small sample * of code points to detect unicode control characters commonly used in binary file signatures. */ static boolean isPlaintext(Buffer buffer) throws EOFException { try { Buffer prefix = new Buffer(); long byteCount = buffer.size() < 64 ? buffer.size() : 64; buffer.copyTo(prefix, 0, byteCount); for (int i = 0; i < 16; i++) { if (prefix.exhausted()) { break; } int codePoint = prefix.readUtf8CodePoint(); if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) { return false; } } return true; } catch (EOFException e) { return false; // Truncated UTF-8 sequence. } } private boolean bodyEncoded(Headers headers) { String contentEncoding = headers.get("Content-Encoding"); return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity"); } }
注意,这里不需要加@Components,因为我在这个控制器里有注入Service和RedisUtil以及配置项,如果直接扫包方式载入,会造成注入对象为null的错误。具体原因是拦截器是在Springcontext之前加载。
新增一个Okhttp的配置类:
@Configuration @ConditionalOnClass(Feign.class) @AutoConfigureBefore(FeignAutoConfiguration.class) @Slf4j public class OkHttpClientConfig { private OkHttpClient okHttpClient; @Bean public OkHttpInterceptor okHttpInterceptor(){ return new OkHttpInterceptor(); } @Bean public okhttp3.OkHttpClient okHttpClient(OkHttpClientFactory okHttpClientFactory, FeignHttpClientProperties httpClientProperties) { this.okHttpClient = okHttpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()).connectTimeout(httpClientProperties.getConnectionTimeout(),TimeUnit.SECONDS) .followRedirects(httpClientProperties.isFollowRedirects()) .addInterceptor(okHttpInterceptor()) .build(); return this.okHttpClient; } }
配置这个类时,我一开始犯了一个错误,抄了网上一段代码,直接在增加拦截器时使用了:
.addInterceptor(new OkHttpInterceptor())
这种方式和为拦截器加注解一样,并不能提前加载拦截器,所以需要如上面的代码,将拦截器独立出来一个bean,将这个bean加入到okhttp即可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· [AI/GPT/综述] AI Agent的设计模式综述