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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM