在进行 HTTP 调用之前使用 RestTemplate 清除 Cookie
Posted
技术标签:
【中文标题】在进行 HTTP 调用之前使用 RestTemplate 清除 Cookie【英文标题】:clear Cookies with RestTemplate before make HTTP-call 【发布时间】:2018-11-13 17:56:10 【问题描述】:我使用 Spring-Boot 2.0.2 RestTemplate
制作以下场景:
我是一名休息消费者(客户),其中:
-
首先需要登录 Spring-Security-Check
然后进行第二次调用以获取数据。
首先,我正在考虑进行身份验证调用并从 SET-COOKIE 手动读取 JSESSIONID Cookie 并将其设置在标题中的第二次调用中。这是我的第一次尝试:
RestClient :
@Service
public class RestClient
private static final String ENDPOINT_DATA = "/data";
private static final String ENDPOINT_SECURITY_CHECK = "/j_spring_security_check";
private static final String HTTP_HEADER_KEY_SET_COOKIE = "Set-Cookie";
private static final String HTTP_HEADER_KEY_COOKIE = "Cookie";
private static final String PROPERTY_SPRING_SECURITY_USER = "j_username";
private static final String PROPERTY_SPRING_SECURITY_PASS = "j_password";
private final RestTemplate restTemplate;
private final RestConfig restConfig;
@Autowired
public RestClient(RestTemplateBuilder restTemplateBuilder, final RestConfig restConfig)
notNull(restTemplateBuilder, "restTemplateBuilder must not be null!");
this.restTemplate = restTemplateBuilder
.additionalCustomizers(new NoRedirectionHandlingRestTemplateCostumizer())
.build();
notNull(restConfig, "openIdConfig must not be null!");
this.restConfig = restConfig;
public String getData()
final String jSessionCockie = jSpringSecurityLogin();
return getAuthorizeCode(jSessionCockie);
private String jSpringSecurityLogin()
// read config
final String fullLoginUri = restConfig.getUrl() + ENDPOINT_SECURITY_CHECK;
final String user = restConfig.getUser();
final String password = restConfig.getPassword();
// Build entity that is send
final HttpHeaders headers = new HttpHeaders();
final String body = PROPERTY_SPRING_SECURITY_USER + "=" + user + "&" + PROPERTY_SPRING_SECURITY_PASS + "=" + password;
final HttpEntity<String> toSend = new HttpEntity<>(body, headers);
final String jSessionIdCockie;
final ResponseEntity<String> response = restTemplate.postForEntity(fullLoginUri, toSend, String.class);
// Get String "JSESSIONID=XXXX". If there are other Cookies, propably will fail.
if (HttpStatus.FOUND.equals(response.getStatusCode()) && response.getHeaders().containsKey(HTTP_HEADER_KEY_SET_COOKIE))
jSessionIdCockie = response.getHeaders().get(HTTP_HEADER_KEY_SET_COOKIE).get(0);
else
throw new Error();
return jSessionIdCockie;
private String getAuthorizeCode(final String jSessionCockie)
// read config
final String fullDataUri = restConfig.getUrl() + ENDPOINT_DATA;
// Build entity that is send
final HttpHeaders headers = new HttpHeaders();
headers.add(HTTP_HEADER_KEY_COOKIE, jSessionCockie);
final HttpEntity<Void> toSend = new HttpEntity<>(headers);
final ResponseEntity<String> response = restTemplate.exchange(fullDataUri, HttpMethod.GET, toSend, String.class);
if (HttpStatus.OK.equals(response.getStatusCode()) && response.hasBody())
return response.getBody();
else
return "";
为了完整起见,这里是构造函数中使用的 Costumizer:
class NoRedirectionHandlingRestTemplateCostumizer implements RestTemplateCustomizer
@Override
public void customize(RestTemplate restTemplate)
final HttpClient httpClient = HttpClientBuilder.create()
.disableRedirectHandling()
.build();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
现在,当我使用 Wiremock 进行一些功能测试时,我在数据调用的 wiremock-reqeust 历史记录中看到以下内容:
...
"headers":
"Cookie": [
"JSESSIONID=axcvueiornxniuwherwuieoiun,asdpfoiu",
"JSESSIONID=axcvueiornxniuwherwuieoiun,asdpfoiu"
],
...
,
"cookies":
"JSESSIONID": [
"axcvueiornxniuwherwuieoiun,asdpfoiu",
"axcvueiornxniuwherwuieoiun,asdpfoiu"
]
...
等待 - JSESSIONID 设置了 2 次。 Cool REST 为我处理!
第二次尝试:我可以删除 Cookie 处理。它有效。
但第一次和第二次尝试有一个问题:在第一次调用RestClient.getData()
之后,对登录端点的所有后续调用也设置了 JSESSIONID。我不知道 Rest-Producer 如何处理这个(会话超时等)。
现在我的问题/疑问:
我想强制RestTemplate
在进行登录调用之前忘记/清除所有 Cookie(之前的原因描述)。有没有可能?我没有找到任何有效的方法。
我现在的解决方法是禁用RestTemplate
的 Cookie 管理,并像第一次尝试一样手动执行所有操作。可以通过RestTemplateCustomizer
禁用 CookieManagment:
class NoRedirectionHandlingRestTemplateCostumizer implements RestTemplateCustomizer
@Override
public void customize(RestTemplate restTemplate)
final HttpClient httpClient = HttpClientBuilder.create()
.disableRedirectHandling()
.disableCookieManagement()
.build();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
【问题讨论】:
你能找到解决方案吗?我面临同样的问题,即使添加解决方法也不起作用。 您好 user964819 。我没有找到其他解决方案,然后是我写的解决方法。如果您想使用标准 OAuth 2,那么 Spring 中有一个实现:请参阅 ***.com/questions/27864295/… 在我的情况下,它不是标准的,这就是重新配置 HttpClient 的黑客攻击 【参考方案1】:正如问题中提到的,首先我们需要通过以下方式禁用cookie管理
CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(厘米) .setRetryHandler(retryHandler) .setKeepAliveStrategy(connectionKeepAliveStrategy) .disableCookieManagement() .evictExpiredConnections().evictIdleConnections(10, TimeUnit.MINUTES) .build();
并像设置任何其他标头一样设置“Cookie”标头。
public ResponseEntity getContent(String url, Map
// add headers
for(String key : headers.keySet())
requestHeaders.set(key, headers.get(key));
// add cookies
List<String> allCookieValues = new ArrayList<>();
for(String key : cookies.keySet())
String value = cookies.get(key);
String cookieString = MessageFormat.format("0=1", key,value);
allCookieValues.add(cookieString);
requestHeaders.addAll(HttpHeaders.COOKIE, allCookieValues);
HttpEntity<String> httpEntity = new HttpEntity<String>(requestHeaders);
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url);
URI uri = builder.build(true).toUri();
ResponseEntity responseEntity = null;
try
responseEntity = restTemplate.exchange(uri, HttpMethod.GET, httpEntity, byte[].class);
…… ......
从响应中,如果你想读取我们可以通过以下方式读取cookies
列出 responseCookies = responseEntity.getHeaders().get(HttpHeaders.SET_COOKIE);
Map
if(requestCookies != null)
cookieNameValue.putAll(requestCookies);
for(String cookie : responseCookies)
String[] cookieParts = cookie.split(";")[0].split("=");
cookieNameValue.put(cookieParts[0], cookieParts[1]);
return cookieNameValue;
【讨论】:
以上是关于在进行 HTTP 调用之前使用 RestTemplate 清除 Cookie的主要内容,如果未能解决你的问题,请参考以下文章