restTemplate重定向问题 &Cookie问题

摘要: 最近在做一个转发功能,zuul + ribbon + resttemplate 进行路由、负载、转发的功能 基本准备就绪,在微信自动登陆那遇到了一个坑,ribbon 系统用resttemplate 转发A系统的资源,在微信自动登陆的地方,A系统重定向到微信的地址,类似下面的代码 结果resttemp阅读全文

最近在做一个转发功能,zuul + ribbon + resttemplate 进行路由、负载、转发的功能

基本准备就绪,在微信自动登陆那遇到了一个坑,ribbon 系统用resttemplate 转发A系统的资源,在微信自动登陆的地方,A系统重定向到微信的地址,类似下面的代码

1
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 自动重定向到本地的地址,如下所示:

1
http://192.168.10.116:**/connect/oauth2/authorize**

仔细思考了下,大概就是resttemplate 的重定向问题,查了查资料,找到一个类HttpComponentsClientHttpRequestFactory,RestTemplate初始化提供了这个类的参数

1
2
3
4
5
6
7
8
9
10
/**
* 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,有如下方法:

第一种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
	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);
}
}
1
2
	NoRedirectClientHttpRequestFactory httpRequestFactory = new NoRedirectClientHttpRequestFactory();
RestTemplate restTemplate = new RestTemplate(httpRequestFactory);

接着,似乎更换ClientHttpRequestFactory并不合心意,还是要使用HttpComponentsClientHttpRequestFactory来实现,HttpComponentsClientHttpRequestFactory是可以自定义HttpClient的,于是查到了HttpClient头上,HttpClient是可以设置Redirect的,

第二种方式:

1
2
3
4
5
	HttpClient httpClient = HttpClientBuilder.create()
.setRedirectStrategy(new LaxRedirectStrategy())
.build();
httpRequestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(httpRequestFactory);

默认提供了两个类,DefaultRedirectStrategy和LaxRedirectStrategy,LaxRedirectStrategy继承自DefaultRedirectStrategy

DefaultRedirectStrategy.java

1
2
3
4
5
6
7
/**
* Redirectable methods.
*/
private static final String[] REDIRECT_METHODS = new String[] {
HttpGet.METHOD_NAME,
HttpHead.METHOD_NAME
};

LaxRedirectStrategy.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
	/*
* ====================================================================
* 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中的定义方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
	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的问题发现了简单的方式

1
HttpClient httpClient = HttpClientBuilder.create().disableCookieManagement().disableRedirectHandling().build();

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
	@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