一、什麼是Cookie
Cookie機制定義了兩種報頭:Set-Cookie報頭和Cookie報頭。
Set-Cookie報頭包含於服務器的響應頭(ResponseHeader)中
Cookie報頭包含在瀏覽器客戶端請求頭(RequestHeader)中
Cookie的運行過程如圖所示,具體分析如下
* setDefault(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呢。?* 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了/**
* 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從容器中移除。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 + "]";
}
}
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();
}
}
}
CookiePolicy.ACCEPT_ALL 接收所有Cookies.
CookiePolicy.ACCEPT_NONE 不接收Cookies.
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.orgpublic 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寫入到文件中
}
}