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即可。

 

posted on 2020-07-09 21:37  zhouyu  阅读(2511)  评论(0编辑  收藏  举报

导航