網絡——Cookie的使用

一、什麼是Cookie

①、Cookie的作用:Cookie是服務端給客戶端頒發的通行證,服務端通過Cookie來確認客戶端的身份。這樣當客戶端操作時候,服務端知道具體修改哪個客戶的數據。而不需要每次客戶端在修改數據的時候,都要輸入一遍,賬號密碼來認證自己。
②、Cookie的傳輸:
Cookie實際上是一小段的文本信息。客戶端請求服務器,如果服務器需要記錄該用戶狀態,就使用response向客戶端瀏覽器頒發一個Cookie。客戶端瀏覽器會把Cookie保存起來。當瀏覽器再請求該網站時,瀏覽器把請求的網址連同該Cookie一同提交給服務器。服務器檢查該Cookie,以此來辨認用戶狀態。服務器還可以根據需要修改Cookie的內容。
那麼服務器怎麼向瀏覽器頒發Cookie呢?
服務器會將Cookie方法Http的請求頭中。然後讓客戶端接收。同樣客戶端也將Cookie放在HTTP請求頭中,返回給客戶端。

Cookie機制定義了兩種報頭:Set-Cookie報頭和Cookie報頭。

Set-Cookie報頭包含於服務器的響應頭(ResponseHeader)中 
Cookie報頭包含在瀏覽器客戶端請求頭(RequestHeader)中

Cookie的運行過程如圖所示,具體分析如下 
這裏寫圖片描述

③、Cookie的主要屬性
String name: Cookie的名字
Object value: Cookie的值
int maxAge:Cookie在客戶端中最大儲存時間(過了這個時間這個Cookie就失效了)
String path:Cookie的使用路徑。例:如果path="session/data",就表示Cookie只能在"session/data"這個路徑下使用(或該路徑下可訪問Cookie)。在其他路徑下使用無效。
String domain:Cookie的使用路徑。例:如果domain=".google.com",就表示只有.google.com結尾的服務器才能收到該Cookie。注意:domain的路徑必須是“.”開頭的。domain與path的區別在於:domain更廣,path更具體。

二、如何在JAVA中處理Cookie
①、主要處理Cookie的類
CookieHandler:用於管理Cookie
CookieManager:繼承了CookieHandler,並且實現了CookieHandler,並將Cookie的任務分成了兩種。一種是Cookie的存儲,另一種是是否允許該Cookie被存儲。
CookieStore:指明瞭Cookie存放的的位置,如果未在CookieManager中設置CookieStore,則會使用默認的CookieStore,存儲在內存中。如果想獲取持久化的Cookie那麼,就需要重寫CookieStore
CookiePolicy:該類用來設置哪類Cookie可以被存儲,哪類Cookie不允許被存儲
HttpCookie:封裝Cookie的類。
關係圖:

②、簡單的獲取網絡中的Cookie
原理:1、首先設置系統的CookieHandler(就是設置默認的CookieHandler)
2、當連接網絡的時候,將服務器返回的Cookie數據存入到CookieHandler中。
3、從CookieHandler中提取查看Cookie

(一)、首先了解CookHandler的方法:
第一組:
* getDefault():獲取默認的CookieHandler
* setDefault(CookieHandler) :設置默認的CookieHandler
通過這樣設置CookieHandler,供全局使用。
第二組:
* get(URI uri,Map<String,List<String>>requestHeaders)  //從CookieHandler中獲取指定url路徑的Cookies,並存儲到requseHeaders這個Map中。
* put(URI uri,Map<String,List<String>>responseHeaders)//將responseHeaders中的數據寫入到CookieHandler中。

(二)、然後設寫一個簡單從網絡中獲取Cookie的方法 (CookieHandler需要自己實現,爲簡便就使用了已經實現CookieHandler的CookieManager代替。等介紹到CookieHandler時候再介紹CookieHandler的實現)
public class Main {
	public static void main(String[]args){
		try {
			//首先:設置默認的Handler
			CookieManager cookieManager = new CookieManager();
			CookieHandler.setDefault(cookieManager);
			//其次:連接網絡
			URL path = new URL("https://www.zhihu.com/");
			HttpURLConnection connection = (HttpURLConnection) path.openConnection();
			//然後:獲取網絡傳輸的數據。(內部其實將Cookie寫入到了CookieHandler中)
			Object data = connection.getContent();
			//從CookieStore中獲取數據
			CookieStore store = cookieManager.getCookieStore();
			List<HttpCookie> cookies = store.getCookies();
			for(HttpCookie cookie: cookies){
				System.out.println(cookie.getName()+"["+cookie.getValue()+"]");
			}			
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
<span style="font-size:18px;">}
</span>
這裏有個疑問:程序是怎麼獲取服務器回覆的數據的報文的header呢?獲取報文的header之後又怎麼獲取header的Cookie呢。?
URLConnection具有提取header報文的方法:
*  getHeaderFields() - 獲取所有的header屬性
*  getHeaderField(String name) - 獲取具體的header屬性
*  getHeaderFieldDate(String name, long default) - 獲取header返回的日期
*  getHeaderFieldInt(String name, int default) - 根據header屬性名,獲取屬性的序列位置
*  getHeaderFieldKey(int n) or getHeaderField(int n) - 根據屬性的序列位置,獲取header

public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			//連接網絡
			URL path = new URL("https://www.zhihu.com/");
			HttpURLConnection connection = (HttpURLConnection) path.openConnection();
			//通過該方法獲取服務器返回數據的報文。 key表示報文的屬性名,value屬性值。
			//爲什麼屬性值是List呢,因爲相同的屬性名可以設置好多個
			Map<String,List<String> > fields =  connection.getHeaderFields();
			//遍歷報文的header
			for(Entry<String, List<String>> field:fields.entrySet()){
				for(String str:field.getValue()){
					System.out.println(field.getKey()+"["+str+"]");
				}
			
			}
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
我們遍歷出了所有的header的屬性。之後我們只要選取屬性名爲set-cookie的屬性值。就可以簡單的拿到cookie了

(三)、自定義CookieHandler和Cookie
通過:自定義CookieHandler和Cookie實現理解Cookie的基本原理
首先自定義CookieHandler
/**
 * 1、繼承CookieHandler類
 * 2、重寫put(),get()接口
 * (未設置如何持久化存儲)
 */
public class MyCookieHandler extends CookieHandler {
	//1、令該容器爲緩存器
	private List<Cookie> cookies = new ArrayList<>();

	/*
	 *1、從Header頭中,獲取Cookie,並創建Cookie
	 *2、將Cookie與容器中的Cookie對比,是否相同,如果相同則替換
	 * 
	 */
	@Override
	public void put(URI uri, Map<String, List<String>> responseHeaders)
			throws IOException {
		// TODO Auto-generated method stub
		//1、從Map中獲取Cookie
		List<String> cookieDatas = responseHeaders.get("Set-Cookie");
		if (cookieDatas == null){
			return;
		}
		//2、將每個Cookie與緩存器中的Cookie對比。
		for(String cookieData:cookieDatas){
			//將data轉化成Cookie
			Cookie newCookie = new Cookie(uri, cookieData);
			//遍歷緩存器中的Cookie
			Iterator<Cookie> iterator = cookies.iterator();
			//存在知識點①
			while(iterator.hasNext()){
				Cookie oldCookie = iterator.next();
				if (oldCookie.matcher(uri) && oldCookie.getName() == newCookie.getName()){
					iterator.remove();
					break;
				}
			}
			cookies.add(newCookie);
		}
		
	}
	
	/*
	 * 1、首先相對應URL的數據,並判斷是否過期,過期則移除
	 * 2、然後將uri相關的Cookie數據,合併成String
	 * 3、將數據加入requestHeaders中
	 * 4、新建Map設置爲不可讀數據,作爲返回值
	 */
	@Override
	public Map<String, List<String>> get(URI uri,
			Map<String, List<String>> requestHeaders) throws IOException {
		// TODO Auto-generated method stub
		StringBuilder builder = new StringBuilder();
		Iterator<Cookie> iterator = cookies.iterator();
		while(iterator.hasNext()){
			Cookie cookie = iterator.next();
			if (cookie.hasExpire()){
				iterator.remove();
			}
			else {
				if (cookie.matcher(uri)){
					builder.append(cookie.toString());
				}
			}
		}
		List<String> singleList = Collections.singletonList(builder.toString());
		Map<String, List<String>> cookieMap = new HashMap<String, List<String>>(requestHeaders);
		cookieMap.put("Cookie", singleList);
		return Collections.unmodifiableMap(cookieMap);
	}
}
知識點①:不允許使用
for(Cookie cookie : cookies){
  if (oldCookie.matcher(uri) && oldCookie.getName() == newCookie.getName()){
	terator.remove();
	break;
  }
  cookies.add(newCookie);
}
因爲:在調用Iterator過程中,容器是無法邊修改邊遍歷的。所以需要通過使用Iterator.remove()來實現將Cookie從容器中移除。

其次自定義Cookie,根據CookieHandler我們知道了Cookie的成員變量與需要的方法
public class Cookie {
	private String name;
	private String value;
	private String domain;
	private String path;
	private Date expireDate;
	private URI uri;	
	//將字符串轉化轉換成日期
	private static DateFormat changeExpire = new SimpleDateFormat("E, dd MMM yyyy k:m:s 'GMT'", Locale.US);
	
	public Cookie(URI uri,String cookieDatas){
		String [] cookieAttrs = cookieDatas.split(";");
		//初始化數據
		String nameValue = cookieAttrs[0].trim();
		String [] cookieEntry = nameValue.split("=");
		this.name = cookieEntry[0];
		this.value = cookieEntry[1];
		this.domain = uri.getHost();
		this.path = "/";
		this.uri = uri;
		this.expireDate = new Date();
		//設定數據
		for(int i=1; i<cookieAttrs.length; ++i){
			nameValue = cookieAttrs[i].trim();
			cookieEntry = nameValue.split("=");
			if (cookieEntry[0].equalsIgnoreCase("domain")){
				this.domain = cookieEntry[1];
			}
			else if (cookieEntry[0].equalsIgnoreCase("path")){
				this.path = path;
			}
			else if (cookieEntry[0].equalsIgnoreCase("expire")){
				try {
					this.expireDate = changeExpire.parse(cookieEntry[1]);
				} catch (ParseException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	//判斷該Cookie是否過期
	public boolean hasExpire(){
		if (expireDate == null){
			return false;
		}
		else {
			Date date = new Date();
			return date.after(this.expireDate);
		}
	}
	//判斷Cookie是否屬於同一個Uri
	public boolean matcher(URI uri){
		if (this.uri.equals(uri)){
			return true;
		}
		else {
			return false;
		}
	}
	//獲取Cookie的名字
	public String getName(){
		return this.name;
	}
	
	@Override
	public String toString() {
		return "[name=" + name + ", value=" + value + ", domain="
				+ domain + ", path=" + path + ", expireDate=" + expireDate
				+ ", uri=" + uri + "]";
	}

}

最後使用(修改原先的CookieManager):
public class Main {
	public static void main(String[]args){
		try {
			//首先:設置默認的Handler
			CookieHandler handler = new MyCookieHandler();
			CookieHandler.setDefault(handler);
			//其次:連接網絡
			URL path = new URL("https://www.zhihu.com/");
			HttpURLConnection connection = (HttpURLConnection) path.openConnection();
			//然後:獲取網絡傳輸的數據。(內部調用了CookieHandler的put()方法)
			Object data = connection.getContent();	
			//再次連接網絡
			URL path2 = new URL("https://www.zhihu.com/");
			//內部調用了CookieHandler的get()方法
			HttpURLConnection connection2 = (HttpURLConnection) path.openConnection();
			
			
			Map<String, List<String>> cookieMap = new HashMap<String, List<String>>();
			try {
				Map<String,List<String>> myMao = handler.get(path.toURI(), cookieMap);
				System.out.println(myMao.get("Cookie"));
			} catch (URISyntaxException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

(四)通過使用CookieManager來設置Cookie
通過剛纔的使用,大致瞭解了CookieManager的使用。現在來詳細瞭解下CookieManager
我們知道CookieManager包含兩個類CookieStore和CookiePolicy。
①、CookiePolicy
系統爲CookiePolicy提供了,幾種屬性
CookiePolicy.ACCEPT_ORIGINAL_SERVER        只接收來自對應Server的cookies.(就是說,服務器反回的response中Set-Cookie選項中必須要有domain屬性,而且這個domain屬性的值必須和請求的url中host相匹配。  
CookiePolicy.ACCEPT_ALL                                    接收所有Cookies.
CookiePolicy.ACCEPT_NONE                                不接收Cookies.

系統默認是使用:CookiePolicy.ACCEPT_ORIGINAL_SERVER 。

自定義CookiePolicy類
public class MyCookiePolicy implements CookiePolicy{
	//構造黑名單列表
	private String[] blackDomain = null;
	
	public MyCookiePolicy(String [] args) {
		// TODO Auto-generated constructor stub
		this.blackDomain = args;
	}
	
	//判斷是否接收該Cookie
	@Override
	public boolean shouldAccept(URI uri, HttpCookie cookie) {
		// TODO Auto-generated method stub
		String host;
		try {
			//首先獲取url的主機名
			host = InetAddress.getByName(uri.getHost()).getCanonicalHostName();
			//然後判斷主機名是否在黑名單中,如果是則拒絕
			for(int i=0; i<blackDomain.length; ++i){
				if(HttpCookie.domainMatches(blackDomain[i], host)){
					return false;
				}
			}
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return true;
	}
}

使用:
public class Main {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		CookieManager cm = new CookieManager();
		cm.setCookiePolicy(new MyCookiePolicy(new String[]{".zhihu.com"}));//<span style="color:#ff0000;">注</span>
		CookieHandler.setDefault(cm);

		try {
			URL url = new URL("https://www.zhihu.com/");
			URLConnection connection = url.openConnection();
			Object obj = connection.getContent();
			CookieStore store = cm.getCookieStore();
			List<HttpCookie> cookies = store.getCookies();
			for(HttpCookie cookie : cookies){
				System.out.println(cookie.getName());
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
注:.zhihu.com可攔截 a.zhihu.com 、b.zhihu.com 但是不可攔截 zhihu.com 或 a.zhuhu.org

②、自定義CookieStore實現Cookie的持久化
步驟1、初始化CookieStore類的時候,從文件中提取Cookie到緩存
       2、使用過程中修改緩存中的Cookie
       3、當類被回收時,將當前的緩存中的Cookie寫入到文件中
public class MyCookieStore implements CookieStore,Runnable{
	private CookieStore  cookieStore;
	public MyCookieStore() {
		// TODO Auto-generated constructor stub
		//通過使用CookeManager自帶的CookieStore簡化步驟
		CookieManager cm = new CookieManager();
		cookieStore = cm.getCookieStore();
		//將緩存中的數據放到cookieStore中
		
		//退出時候將緩存數據存儲到文件中
		Runtime.getRuntime().addShutdownHook(new Thread(this));
	}
	
	@Override
	public void add(URI uri, HttpCookie cookie) {
		// TODO Auto-generated method stub
		cookieStore.add(uri, cookie);
	}

	@Override
	public List<HttpCookie> get(URI uri) {
		// TODO Auto-generated method stub
		return cookieStore.get(uri);
	}

	@Override
	public List<HttpCookie> getCookies() {
		// TODO Auto-generated method stub
		return cookieStore.getCookies();
	}

	@Override
	public List<URI> getURIs() {
		// TODO Auto-generated method stub
		return cookieStore.getURIs();
	}

	@Override
	public boolean remove(URI uri, HttpCookie cookie) {
		// TODO Auto-generated method stub
		return cookieStore.remove(uri, cookie);
	}

	@Override
	public boolean removeAll() {
		// TODO Auto-generated method stub
		return cookieStore.removeAll();
	}


	@Override
	public void run() {
		// TODO Auto-generated method stub
		//將當前緩存中的Cookie寫入到文件中
	}
}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章