最近在做一个转发功能,zuul + ribbon + resttemplate 进行路由、负载、转发的功能
基本准备就绪,在微信自动登陆那遇到了一个坑,ribbon 系统用resttemplate 转发A系统的资源,在微信自动登陆的地方,A系统重定向到微信的地址,类似下面的代码
redirect:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx3290f3d5****&redirect_uri=http://***.com/weixin/wxAuthRedirect?redirectUrl=http%3A%2F%2F192.168.10.116%3A8081%2Finternal%2Fpage%2Fuser%2Flogin_wx&response_type=code&scope=snsapi_userinfo&state=state#wechat_redirect
结果resttemplate 自动重定向到本地的地址,如下所示:
http://192.168.10.116:**/connect/oauth2/authorize**
仔细思考了下,大概就是resttemplate 的重定向问题,查了查资料,找到一个类HttpComponentsClientHttpRequestFactory,RestTemplate初始化提供了这个类的参数
/** * Create a new instance of the {@link RestTemplate} based on the given {@link ClientHttpRequestFactory}. * @param requestFactory HTTP request factory to use * @see org.springframework.http.client.SimpleClientHttpRequestFactory * @see org.springframework.http.client.HttpComponentsClientHttpRequestFactory */ public RestTemplate(ClientHttpRequestFactory requestFactory) { this(); setRequestFactory(requestFactory); }
HttpComponentsClientHttpRequestFactory继承自ClientHttpRequestFactory,这个类的子类有HttpComponentsClientHttpRequestFactory和SimpleClientHttpRequestFactory
找到SimpleClientHttpRequestFactory,有如下方法:
第一种方式:
/** * Template method for preparing the given {@link HttpURLConnection}. * <p>The default implementation prepares the connection for input and output, and sets the HTTP method. * @param connection the connection to prepare * @param httpMethod the HTTP request method ({@code GET}, {@code POST}, etc.) * @throws IOException in case of I/O errors */ protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException { if (this.connectTimeout >= 0) { connection.setConnectTimeout(this.connectTimeout); } if (this.readTimeout >= 0) { connection.setReadTimeout(this.readTimeout); } connection.setDoInput(true); if ("GET".equals(httpMethod)) { connection.setInstanceFollowRedirects(true); } else { connection.setInstanceFollowRedirects(false); } if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) || "PATCH".equals(httpMethod) || "DELETE".equals(httpMethod)) { connection.setDoOutput(true); } else { connection.setDoOutput(false); } connection.setRequestMethod(httpMethod); }
可以看到setInstanceFollowRedirects,get请求是可以重定向的,其他方法禁止了重定向,于是建个SimpleClientHttpRequestFactory的子类,禁用重定向。
于是乎 NoRedirectClientHttpRequestFactory.java
import java.io.IOException; import java.net.HttpURLConnection; import org.springframework.http.client.SimpleClientHttpRequestFactory; public class NoRedirectClientHttpRequestFactory extends SimpleClientHttpRequestFactory { @Override protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException { // TODO Auto-generated method stub super.prepareConnection(connection, httpMethod); // 禁止自动重定向 connection.setFollowRedirects(false); } }
NoRedirectClientHttpRequestFactory httpRequestFactory = new NoRedirectClientHttpRequestFactory(); RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
接着,似乎更换ClientHttpRequestFactory并不合心意,还是要使用HttpComponentsClientHttpRequestFactory来实现,HttpComponentsClientHttpRequestFactory是可以自定义HttpClient的,于是查到了HttpClient头上,HttpClient是可以设置Redirect的,
第二种方式:
HttpClient httpClient = HttpClientBuilder.create() .setRedirectStrategy(new LaxRedirectStrategy()) .build(); httpRequestFactory.setHttpClient(httpClient); RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
默认提供了两个类,DefaultRedirectStrategy和LaxRedirectStrategy,LaxRedirectStrategy继承自DefaultRedirectStrategy
DefaultRedirectStrategy.java
/** * Redirectable methods. */ private static final String[] REDIRECT_METHODS = new String[] { HttpGet.METHOD_NAME, HttpHead.METHOD_NAME };
LaxRedirectStrategy.java
/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.http.impl.client; import org.apache.http.annotation.Contract; import org.apache.http.annotation.ThreadingBehavior; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpHead; import org.apache.http.client.methods.HttpPost; /** * Lax {@link org.apache.http.client.RedirectStrategy} implementation * that automatically redirects all HEAD, GET, POST, and DELETE requests. * This strategy relaxes restrictions on automatic redirection of * POST methods imposed by the HTTP specification. * * @since 4.2 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class LaxRedirectStrategy extends DefaultRedirectStrategy { public static final LaxRedirectStrategy INSTANCE = new LaxRedirectStrategy(); /** * Redirectable methods. */ private static final String[] REDIRECT_METHODS = new String[] { HttpGet.METHOD_NAME, HttpPost.METHOD_NAME, HttpHead.METHOD_NAME, HttpDelete.METHOD_NAME }; @Override protected boolean isRedirectable(final String method) { for (final String m: REDIRECT_METHODS) { if (m.equalsIgnoreCase(method)) { return true; } } return false; } }
这就很清晰了,copy一份LaxRedirectStrategy的代码,改写掉REDIRECT_METHODS中的定义方法,如下:
import org.apache.http.annotation.Contract; import org.apache.http.annotation.ThreadingBehavior; import org.apache.http.impl.client.DefaultRedirectStrategy; /** * * @ClassName: MyRedirectStrategy * @Description: TODO * @author thinklight * @date 2018年4月20日 下午2:47:29 * */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class MyRedirectStrategy extends DefaultRedirectStrategy { public static final MyRedirectStrategy INSTANCE = new MyRedirectStrategy(); /** * Redirectable methods. */ private static final String[] REDIRECT_METHODS = new String[] {}; @Override protected boolean isRedirectable(final String method) { for (final String m: REDIRECT_METHODS) { if (m.equalsIgnoreCase(method)) { return true; } } return false; } }
ribbon+微信各种重定向问题,解决了。
第三种方式:
自己蠢了,今天因为cookie的问题发现了简单的方式
HttpClient httpClient = HttpClientBuilder.create().disableCookieManagement().disableRedirectHandling().build();
完整代码如下:
@Autowired RestTemplate restTemplate; @Bean @LoadBalanced RestTemplate restTemplate() { HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory(); // NoRedirectClientHttpRequestFactory httpRequestFactory = new NoRedirectClientHttpRequestFactory();// 此类型不能使用httpClient httpRequestFactory.setConnectionRequestTimeout(2000); httpRequestFactory.setConnectTimeout(10000); httpRequestFactory.setReadTimeout(7200000); // HttpClient httpClient = HttpClientBuilder.create() // .setRedirectStrategy(new MyRedirectStrategy()) // .build();
HttpClient httpClient = HttpClientBuilder.create().disableCookieManagement().disableRedirectHandling().build(); httpRequestFactory.setHttpClient(httpClient); RestTemplate restTemplate = new RestTemplate(httpRequestFactory); logger.debug("指定字符编码为UTF-8,原编码为ISO-8859-1"); restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); logger.debug("RestTemple默认能转换为application/json,转换追加text/plain类型"); restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter()); return restTemplate; }
重定向参考:https://www.dozer.cc/2014/05/disable-resttemplate-redirect.html
cookie参考:https://stackoverflow.com/questions/10175649/resttemplate-and-cookie
https://stackoverflow.com/questions/22853321/resttemplate-client-with-cookies