Здравствуйте. Пишу программу, для работы с которой необходимо парсить страницу на сайте и выбирать необходимую информацию. Столкнулся со следующей проблемой : портал, с которого парсится информация с автоматической авторизацией пользователя. При просмотре в браузере google chrome информации о cookie написано следующее : В качестве cookie используется phpsessionid. Срок действия - при завершении сеанса браузера. Проблема в том, что при последовательных get запросах через некоторое время от сервера приходит ответ 401 - не авторизован. После определенного времени можно снова пользоваться запросами, пока снова будем не авторизованы. Использую стандартные URLConnection. Получаю из заголовка Set-Cookie значение сессии, подставляю в последующие запросы - все равно такая проблема. код : Код | /** * CookieManager is a simple utilty for handling cookies when working * with java.net.URL and java.net.URLConnection * objects. * * * Cookiemanager cm = new CookieManager(); * URL url = new URL("http://www.hccp.org/test/cookieTest.jsp"); * * . . . * * // getting cookies: * URLConnection conn = url.openConnection(); * conn.connect(); * * // setting cookies * cm.storeCookies(conn); * cm.setCookies(url.openConnection()); * * @author Ian Brown * **/
public class CookieManager { private Map store;
private static final String SET_COOKIE = "Set-Cookie"; private static final String COOKIE_VALUE_DELIMITER = ";"; private static final String PATH = "path"; private static final String EXPIRES = "expires"; private static final String DATE_FORMAT = "EEE, dd-MMM-yyyy hh:mm:ss z"; private static final String SET_COOKIE_SEPARATOR="; "; private static final String COOKIE = "Cookie";
private static final char NAME_VALUE_SEPARATOR = '='; private static final char DOT = '.'; private DateFormat dateFormat;
public CookieManager() {
store = new HashMap(); dateFormat = new SimpleDateFormat(DATE_FORMAT); }
/** * Retrieves and stores cookies returned by the host on the other side * of the the open java.net.URLConnection. * * The connection MUST have been opened using the connect() * method or a IOException will be thrown. * * @param conn a java.net.URLConnection - must be open, or IOException will be thrown * @throws java.io.IOException Thrown if conn is not open. */ public void storeCookies(URLConnection conn) throws IOException { // let's determine the domain from where these cookies are being sent String domain = getDomainFromHost(conn.getURL().getHost()); Map domainStore; // this is where we will store cookies for this domain // now let's check the store to see if we have an entry for this domain if (store.containsKey(domain)) { // we do, so lets retrieve it from the store domainStore = (Map)store.get(domain); } else { // we don't, so let's create it and put it in the store domainStore = new HashMap(); store.put(domain, domainStore); } // OK, now we are ready to get the cookies out of the URLConnection String headerName=null; for (int i=1; (headerName = conn.getHeaderFieldKey(i)) != null; i++) { if (headerName.equalsIgnoreCase(SET_COOKIE)) { Map cookie = new HashMap(); StringTokenizer st = new StringTokenizer(conn.getHeaderField(i), COOKIE_VALUE_DELIMITER); // the specification dictates that the first name/value pair // in the string is the cookie name and value, so let's handle // them as a special case: if (st.hasMoreTokens()) { String token = st.nextToken(); String name = token.substring(0, token.indexOf(NAME_VALUE_SEPARATOR)); String value = token.substring(token.indexOf(NAME_VALUE_SEPARATOR) + 1, token.length()); domainStore.put(name, cookie); cookie.put(name, value); } while (st.hasMoreTokens()) { String token = st.nextToken(); if(token.indexOf(NAME_VALUE_SEPARATOR) != -1) cookie.put(token.substring(0, token.indexOf(NAME_VALUE_SEPARATOR)).toLowerCase(), token.substring(token.indexOf(NAME_VALUE_SEPARATOR) + 1, token.length())); } } } }
/** * Prior to opening a URLConnection, calling this method will set all * unexpired cookies that match the path or subpaths for thi underlying URL * * The connection MUST NOT have been opened * method or an IOException will be thrown. * * @param conn a java.net.URLConnection - must NOT be open, or IOException will be thrown * @throws java.io.IOException Thrown if conn has already been opened. */ public void setCookies(URLConnection conn) throws IOException { // let's determine the domain and path to retrieve the appropriate cookies URL url = conn.getURL(); String domain = getDomainFromHost(url.getHost()); String path = url.getPath(); Map domainStore = (Map)store.get(domain); if (domainStore == null) return; StringBuffer cookieStringBuffer = new StringBuffer(); Iterator cookieNames = domainStore.keySet().iterator(); while(cookieNames.hasNext()) { String cookieName = (String)cookieNames.next(); Map cookie = (Map)domainStore.get(cookieName); // check cookie to ensure path matches and cookie is not expired // if all is cool, add cookie to header string if (comparePaths((String)cookie.get(PATH), path) && isNotExpired((String)cookie.get(EXPIRES))) { cookieStringBuffer.append(cookieName); cookieStringBuffer.append("="); cookieStringBuffer.append((String)cookie.get(cookieName)); if (cookieNames.hasNext()) cookieStringBuffer.append(SET_COOKIE_SEPARATOR); } } try { conn.setRequestProperty(COOKIE, cookieStringBuffer.toString()); } catch (java.lang.IllegalStateException ise) { IOException ioe = new IOException("Illegal State! Cookies cannot be set on a URLConnection that is already connected. " + "Only call setCookies(java.net.URLConnection) AFTER calling java.net.URLConnection.connect()."); throw ioe; } }
private String getDomainFromHost(String host) { if (host.indexOf(DOT) != host.lastIndexOf(DOT)) { return host.substring(host.indexOf(DOT) + 1); } else { return host; } }
private boolean isNotExpired(String cookieExpires) { if (cookieExpires == null) return true; Date now = new Date(); try { return (now.compareTo(dateFormat.parse(cookieExpires))) <= 0; } catch (java.text.ParseException pe) { pe.printStackTrace(); return false; } }
private boolean comparePaths(String cookiePath, String targetPath) { if (cookiePath == null) { return true; } else if (cookiePath.equals("/")) { return true; } else if (targetPath.regionMatches(0, cookiePath, 0, cookiePath.length())) { return true; } else { return false; } } /** * Returns a string representation of stored cookies organized by domain. */
public String toString() { return store.toString(); } public static void main(String[] args) { CookieManager cm = new CookieManager(); try { URL url = new URL("http://www.google.com"); URLConnection conn = url.openConnection(); conn.connect(); cm.storeCookies(conn); System.out.println(cm); cm.setCookies(url.openConnection()); } catch (IOException ioe) { ioe.printStackTrace(); } } }
|
После нашел в интернете пример с классом CookieHandler Код | package com.samples.cach;
import java.io.*; import java.net.*; import java.util.*;
public class ListCookieHandler extends CookieHandler {
private List<Cookie> cache = new LinkedList();
/** * Сохранение всех cookie, находящихся в заголовках ответа * в кеше. * @param uri URI источник cookie * @param responseHeaders Неизменяемая таблица, преобразованная * в список, представляющий поля заголовка ответа */
public void put( URI uri, Map<String, List<String>> responseHeaders) throws IOException {
System.out.println("Cache: " + cache); List<String> setCookieList = responseHeaders.get("Set-Cookie"); if (setCookieList != null) { for (String item : setCookieList) { Cookie cookie = new Cookie(uri, item); // Удаления уже существующих cookie // Установка новых щначений for (Cookie existingCookie : cache) { if((cookie.getURI().equals( existingCookie.getURI())) && (cookie.getName().equals( existingCookie.getName()))) { cache.remove(existingCookie); break; } } System.out.println("Adding to cache: " + cookie); cache.add(cookie); } } }
/** * Получение всех cookie из кеша, соответствующих * uri в заголовке запроса. * * @param uri URI для отправки cookie * @param requestHeaders Таблица заголовков запроса, преобразованная * список полей, содержащих заголовки запроса */
public Map<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders) throws IOException {
// Получение cookie для соответствующего URI // Сохранение в списке, разделенном запятыми StringBuilder cookies = new StringBuilder(); for (Cookie cookie : cache) { // Удаление просроченных cookie if (cookie.hasExpired()) { cache.remove(cookie); } else if (cookie.matches(uri)) { if (cookies.length() > 0) { cookies.append(", "); } cookies.append(cookie.toString()); } }
// Возвращаемая таблица Map<String, List<String>> cookieMap = new HashMap(requestHeaders);
// Преобразование StringBuilder в List, для сохранения в таблице if (cookies.length() > 0) { List list = Collections.singletonList(cookies.toString()); cookieMap.put("Cookie", list); } System.out.println("Cookies: " + cookieMap); return Collections.unmodifiableMap(cookieMap); }
}
|
Код | package com.samples.cach;
import java.net.*; import java.text.*; import java.util.*;
public class Cookie {
String name; String value; URI uri; String domain; Date expires; String path;
private static DateFormat expiresFormat1 = new SimpleDateFormat("E, dd MMM yyyy k:m:s 'GMT'");
private static DateFormat expiresFormat2 = new SimpleDateFormat("E, dd-MMM-yyyy k:m:s 'GMT'");
/** * Создание cookie из URI и полей заголовка * * @param uri URI для cookie * @param header установка атрибутов заголовка */ public Cookie(URI uri, String header) { String attributes[] = header.split(";"); String nameValue = attributes[0].trim(); this.uri = uri; this.name = nameValue.substring(0, nameValue.indexOf('=')); this.value = nameValue.substring(nameValue.indexOf('=')+1); this.path = "/"; this.domain = uri.getHost();
for (int i=1; i < attributes.length; i++) { nameValue = attributes[i].trim(); int equals = nameValue.indexOf('='); if (equals == -1) { continue; } String name = nameValue.substring(0, equals); String value = nameValue.substring(equals+1); if (name.equalsIgnoreCase("domain")) { String uriDomain = uri.getHost(); if (uriDomain.equals(value)) { this.domain = value; } else { if (!value.startsWith(".")) { value = "." + value; } uriDomain = uriDomain.substring( uriDomain.indexOf('.')); if (!uriDomain.equals(value)) { throw new IllegalArgumentException( "Trying to set foreign cookie"); } this.domain = value; } } else if (name.equalsIgnoreCase("path")) { this.path = value; } else if (name.equalsIgnoreCase("expires")) { try { this.expires = expiresFormat1.parse(value); } catch (ParseException e) { try { this.expires = expiresFormat2.parse(value); } catch (ParseException e2) { throw new IllegalArgumentException( "Bad date format in header: " + value); } } } } }
public boolean hasExpired() { if (expires == null) { return false; } Date now = new Date(); return now.after(expires); }
public String getName() { return name; }
public URI getURI() { return uri; }
/** * Проверка срока действия cookie и соответствие URI * * @param uri URI для проверки * @return true в случае соответствия, иначе false */ public boolean matches(URI uri) {
if (hasExpired()) { return false; }
String path = uri.getPath(); if (path == null) { path = "/"; }
return path.startsWith(this.path); }
public String toString() { StringBuilder result = new StringBuilder(name); result.append("="); result.append(value); return result.toString(); } }
|
Код | package com.samples.cach;
import java.net.CookieHandler; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection;
public class TestCookie {
/** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { String urlString = "http://java.sun.com"; CookieHandler.setDefault(new ListCookieHandler()); URL url = new URL(urlString); URLConnection connection = url.openConnection(); Object obj = connection.getContent(); url = new URL(urlString); connection = url.openConnection(); obj = connection.getContent(); }
}
|
Вижу, что cookie берутся, устанавливаются и зацикливаеются методы put() и get() класса ListCookieHandler, cookie устанавливаются пока не придет опять опять ответ от сервера 401. Подскажите, пожалуйста, в какую сторону копать ? Я правильно понимаю, раз сертификат действует в пределах одной сессии, а объект URLConnection является одноразовым, то у меня фактически каждый запрос является отдельным открыванием браузера и получения страницы ? Т.е мои манипуляции бесполезны. Спасибо.
|