http协议 cookie设置

6 篇文章 0 订阅
在bs的架构中,浏览器作为客户端,与服务器之间通过session保持连接状态。
以前面试的时候,经常被问及一个问题:浏览器禁止cookie时,服务器与客户端浏览器能否保持session连接?

其实要完全回答正确这个问题,需要对cookie的作用有全面的了解。
具体cookie的解释大家可以google一下。我以前一直有一个误解(估计很多人都有),
以为cookie就是一个文件,用来保存用户信息或者其他信息。如果这么认为的话,
那么浏览器禁用cookie也就是不保存成文件而已,并不影响浏览器与服务器交互。
其实如果我们看看浏览器发出的http请求消息和服务器的http响应消息就一目了然了。

Http request:
GET /cluster/index.jsp HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*
Accept-Language: zh-cn
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; InfoPath.2; CIBA; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Accept-Encoding: gzip, deflate
Host: localhost:8080
Connection: Keep-Alive

Http response:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=4918D6ED22B81B587E7AF7517CE24E25.server1; Path=/cluster
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 377
Date: Tue, 02 Mar 2010 02:58:32 GMT

这是我们打开浏览器后,第一次请求一个网址时的请求和响应消息(如果客户本地没有该网址的cookie)。
首先看response消息,可以看到一行醒目的字符串Set-Cookie: JSESSIONID=1363B650B295DE9494A33805F62BC5ED.server1; Path=/cluster。
其实这行是在告诉客户端浏览器,把这段cookie保存下来,根据cookie的存活时间,
这段cookie信息有可能只存在在内存中,也可能保存到文件中。下面不关闭浏览器,再次请求同样的url,观察第二次的请求和响应消息:

Http request:
GET /cluster/user_details.jsp HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*
Accept-Language: zh-cn
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; InfoPath.2; CIBA; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Accept-Encoding: gzip, deflate
Host: localhost:8080
Connection: Keep-Alive
Cookie: JSESSIONID=4918D6ED22B81B587E7AF7517CE24E25.server1

Http response
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=4918D6ED22B81B587E7AF7517CE24E25.server1; Expires=Tue, 02-Mar-2010 22:15:38 GMT
Content-Type: text/html
Content-Length: 252
Date: Tue, 02 Mar 2010 05:35:38 GMT

这个时候注意看请求信息中多了一行Cookie,浏览器将这个Cookie信息传递给服务器,这次服务器返回的Set-Cookie中的JSESSIONID就和请求的JSESSIONID一致了。
通过这两次实验,总结出以下几点:
1.session是什么?session其实是在无状态会话的http协议下,为了实现有状态会话而设计的一种实现方法。
2.如何实现session会话?简单的说,就是在服务器端创建session对象,记录会话信息,同时在客户端与服务器的交互过程中,
    通过JSESSIONID字符串唯一标识session对象(客户端只需要保存JSESSIONID这个字符串,
    服务器端只需要保存JSESSIONID和真实的session对象之间的关联关系),
    而这个JSESSIONID字符串就是通过http协议的cookie功能实现的。
3.浏览器禁用cookie时会发生什么?浏览器禁止了Cookie,那么第二次请求中将不包含有Cookie信息(当然更不存在JSESSIONID信息),
    服务器也就不会收到JSESSIONID的值,于是服务器认为是新请求,又创建一个服务器session,
    同时将本次生成的JSESSIONID再次通过响应信息中的Set-Cookie返回给客户端。如此循环交互,
    服务器将永远认为该客户浏览器的请求是新请求,因此无法实现session功能。
  最后回到最初的那个面试问题:浏览器禁止cookie时,服务器与客户端浏览器能否保持session连接?
当然可以,但是现在不能依靠浏览器自动完成这个功能,而是需要服务器端通过重写URL的方法来实现,
就是在返回的页面中,在URL里将JSESSIONID的值作为请求参数传递给服务器端,
例如<a href="http://www.java.com;JSESSIONID=4918D6ED22B81B587E7AF7517CE24E25">。



*************************************************************
Java SE 6 新特性: HTTP 增强
http://www.ibm.com/developerworks/cn/java/j-lo-jse62/

轻量级 HTTP 服务器

Java 6 还提供了一个轻量级的纯 Java Http 服务器的实现。下面是一个简单的例子:

 public static void main(String[] args) throws Exception{
     HttpServerProvider httpServerProvider = HttpServerProvider.provider();
     InetSocketAddress addr = new InetSocketAddress(7778);
     HttpServer httpServer = httpServerProvider.createHttpServer(addr, 1);
     httpServer.createContext("/myapp/", new MyHttpHandler());
     httpServer.setExecutor(null);
     httpServer.start();
     System.out.println("started");
 }

 static class MyHttpHandler implements HttpHandler{
     public void handle(HttpExchange httpExchange) throws IOException {          
         String response = "Hello world!";
         httpExchange.sendResponseHeaders(200, response.length());
         OutputStream out = httpExchange.getResponseBody();
         out.write(response.getBytes());
         out.close();
     }  
 }

然后,在浏览器中访问 http://localhost:7778/myapp/,我们得到:
Hello World

首先,HttpServer 是从 HttpProvider 处得到的,
这里我们使用了 JDK 6 提供的实现。
用户也可以自行实现一个 HttpProvider 和相应的 HttpServer 实现。

其次,HttpServer 是有上下文(context)的概念的。
比如,http://localhost:7778/myapp/ 中“/myapp/”就是相对于 HttpServer Root 的上下文。
对于每个上下文,都有一个 HttpHandler 来接收 http 请求并给出回答。

最后,在 HttpHandler 给出具体回答之前,一般先要返回一个 Http head。
这里使用 HttpExchange.sendResponseHeaders(int code, int length)。
其中 code 是 Http 响应的返回值,比如那个著名的 404。length 指的是 response 的长度,以字节为单位。




使用jetty 自带的http client!
一般情况下我们需要使用HttpClient时可供选择的技术有:
1、HttpURLConnection
2、Apache HttpClient
其他的除了写Socket 我都没有用过了。

偶然的机会发现Jetty 里面也自带了一个HttpClient,并且支持事件触发的处理方式。
    HttpClient client = new HttpClient();  
    client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);  
    try  
    {  
      client.start();  
    }  
    catch (Exception e)  
    {  
      throw new ServletException(e);  
    }  
      
    // create the exchange object, which lets you define where you want to go  
    // and what you want to do once you get a response  
    ContentExchange exchange = new ContentExchange()  
    {  
      // define the callback method to process the response when you get it back  
      protected void onResponseComplete() throws IOException  
      {  
        super.onResponseComplete();  
        String responseContent = this.getResponseContent();  
      
        // do something with the response content  
        ...  
      }  
    };  
      
    exchange.setMethod("GET");  
    exchange.setURL("http://www.example.com/");  
      
    // start the exchange  
    client.send(exchange);

    
还可以使用同步的处理方式:
    public static void main(String[] args)  
            {  
            HttpClient httpClient = new HttpClient();  
                    //set up httpClient  
                    httpClient.start();  
            ContentExchange contentExchange = new ContentExchange();  
                    httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);  
            contentExchange.setURL("http://slashdot.org");  
                    httpClient.send();  
                    contentExchange.waitForDone();  
            System.err.println("Response status: "+contentExchange.getResponseStatus());  
        }

还可以方便的设置Proxy网关:
    client.setProxy(new Address("proxy_address",proxy_port));  
    client.setProxyAuthentication(new ProxyAuthorization("user","password"));  


    

Class HttpExchange
http://archive.eclipse.org/jetty/8.1.0.RC5/apidocs/org/eclipse/jetty/client/HttpExchange.html
http://docs.oracle.com/javase/7/docs/api/javax/xml/ws/spi/http/HttpExchange.html
http://archive.eclipse.org/jetty/8.1.0.RC5/apidocs/org/eclipse/jetty/client/HttpExchange.html

类 HttpExchange.ContentExchange
http://www.boyunjian.com/javadoc/org.testatoo.container/testatoo-container-jetty8/2.1.ga/embedded/org/eclipse/jetty/client/HttpExchange.ContentExchange.html
     java.lang.Object
        org.eclipse.jetty.client.HttpExchange
            org.eclipse.jetty.client.CachedExchange
                org.eclipse.jetty.client.ContentExchange
                    org.eclipse.jetty.client.HttpExchange.ContentExchange


                    
                    
方法概要
从类继承的方法 org.eclipse.jetty.client.ContentExchange
getFileForUpload, getResponseContent, getResponseContentBytes, onResponseContent, onResponseHeader, onResponseStatus, onRetry, setFileForUpload

从类继承的方法 org.eclipse.jetty.client.CachedExchange
getResponseFields, getResponseStatus

从类继承的方法 org.eclipse.jetty.client.HttpExchange
addRequestHeader, addRequestHeader,
cancel, cancelTimeout, configureListeners,
expire,
getAddress, getEventListener, getLocalAddress, getMethod,
getRequestContent, getRequestContentChunk, getRequestContentSource, getRequestFields, getRequestURI,
getRetryStatus, getScheme, getStatus, getTimeout, getURI, getVersion,
isDone, isDone,
onConnectionFailed, onException, onExpire,
onRequestCommitted, onRequestComplete, onResponseComplete, onResponseHeaderComplete, onSwitchProtocol,
reset, scheduleTimeout, setAddress, setConfigureListeners, setEventListener, setMethod,
setRequestContent, setRequestContentSource, setRequestContentType, setRequestHeader, setRequestHeader, setRequestURI,
setRetryStatus, setScheme, setScheme, setTimeout, setURI, setURI, setURL, setVersion, setVersion,
toState, toString, waitForDone, waitForStatus
                    
                    
Class JettyContentExchange
java.lang.Object
  extended by org.eclipse.jetty.client.HttpExchange
      extended by org.eclipse.jetty.client.CachedExchange
          extended by org.eclipse.jetty.client.ContentExchange
              extended by org.apache.camel.component.jetty.JettyContentExchange
http://camel.apache.org/maven/camel-2.11.0/camel-jetty/apidocs/org/apache/camel/component/jetty/JettyContentExchange.html                    
Method Summary
protected  void     closeRequestContentSource()       
protected  void     doTaskCompleted()           
protected  void     doTaskCompleted(Throwable ex)           
           byte[]     getBody()           
Map<String,String>     getHeaders()           
           String     getUrl()          
protected  void     onConnectionFailed(Throwable ex)          
protected  void     onException(Throwable ex)          
protected  void     onExpire()           
protected  void     onRequestComplete()           
protected  void     onResponseComplete()           
protected  void     onResponseHeader(org.eclipse.jetty.io.Buffer name, org.eclipse.jetty.io.Buffer value)           
           void     setCallback(org.apache.camel.AsyncCallback callback)                    
                    
                    
Field Summary
static int     STATUS_CANCELLED      
static int     STATUS_CANCELLING           
static int     STATUS_COMPLETED           
static int     STATUS_EXCEPTED           
static int     STATUS_EXPIRED           
static int     STATUS_PARSING_CONTENT           
static int     STATUS_PARSING_HEADERS           
static int     STATUS_SENDING_REQUEST           
static int     STATUS_START           
static int     STATUS_WAITING_FOR_COMMIT           
static int     STATUS_WAITING_FOR_CONNECTION           
static int     STATUS_WAITING_FOR_RESPONSE                    
                    
                    
                    
                    
*************************************************************
Cookie 管理特性

http://www.open-open.com/lib/view/open1405483246415.html

虽然,我们不能把一些特别敏感的信息存放在 Cookie 里面,
但是,Cookie 依然可以帮助我们储存一些琐碎的信息,
帮助 Web 用户在访问网页时获得更好的体验,例如个人的搜索参数,颜色偏好以及上次的访问时间等等。
网络程序开发者可以利用 Cookie 来创建有状态的网络会话(Stateful Session)。
到了 Java SE 6, Cookie 相关的管理类在 Java 类库里面才得到了实现。
有了这些 Cookie 相关支持的类,Java 开发者可以在服务器端编程中很好的操作 Cookie,
更好的支持 HTTP 相关应用,创建有状态的 HTTP 会话。
用 HttpCookie 代表 Cookie

java.net.HttpCookie 类是 Java SE 6 新增的一个表示 HTTP Cookie 的新类, 其对象可以表示 Cookie 的内容
这个类储存了 Cookie 的名称,路径,值,协议版本号,是否过期,网络域,最大生命期等等信息。
用 CookiePolicy 规定 Cookie 接受策略

java.net.CookiePolicy 接口可以规定 Cookie 的接受策略。
其中唯一的方法用来判断某一特定的 Cookie 是否能被某一特定的地址所接受。
这个类内置了 3 个实现的子类。一个类接受所有的 Cookie,另一个则拒绝所有,还有一个类则接受所有来自原地址的 Cookie。

用 CookieStore 储存 Cookie
java.net.CookieStore 接口负责储存和取出 Cookie。
当有 HTTP 请求的时候,它便储存那些被接受的 Cookie;
当有 HTTP 回应的时候,它便取出相应的 Cookie。
另外,当一个 Cookie 过期的时候,它还负责自动删去这个 Cookie。
用 CookieManger/CookieHandler 管理 Cookie

java.net.CookieManager 是整个 Cookie 管理机制的核心,
它是 CookieHandler 的默认实现子类。下图显示了整个 HTTP Cookie 管理机制的结构:

CookieHandler <--------- HttpURLConnection
    ^
    |
    |
CookieManager ----------> CookiePolicy
            |
            |
            | ----------> HttpCookie
            |                ^
            |                |
            |                |
            |----------> CookieStore
                            ^
                            |
                            |
                        internal in-memory implementation

下面这个简单的例子说明了 Cookie 相关的管理功能:

 // 创建一个默认的 CookieManager
 CookieManager manager = new CookieManager();

 // 将规则改掉,接受所有的 Cookie
 manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);

 // 保存这个定制的 CookieManager
 CookieHandler.setDefault(manager);
        
 // 接受 HTTP 请求的时候,得到和保存新的 Cookie
 HttpCookie cookie = new HttpCookie("...(name)...","...(value)...");
 manager.getCookieStore().add(uri, cookie);
        
 // 使用 Cookie 的时候:
 // 取出 CookieStore        
 CookieStore store = manager.getCookieStore();

 // 得到所有的 URI        
 List<URI> uris = store.getURIs();
 for (URI uri : uris) {
     // 筛选需要的 URI
     // 得到属于这个 URI 的所有 Cookie
     List<HttpCookie> cookies = store.get(uri);
     for (HttpCookie cookie : cookies) {
         // 取出了 Cookie
     }
 }
        
 // 或者,取出这个 CookieStore 里面的全部 Cookie
 // 过期的 Cookie 将会被自动删除
 List<HttpCookie> cookies = store.getCookies();
 for (HttpCookie cookie : cookies) {
     // 取出了 Cookie
 }

 
 
 
URL地址重写
URL地址重写是对客户端不支持Cookie的解决方案。URL地址重写的原理是将该用户Session的id信息重写到URL地址中。
服务器能够解析重写后的URL获取Session的id。这样即使客户端不支持Cookie,
也可以使用Session来记录用户状态。HttpServletResponse类提供了encodeURL(Stringurl)实现URL地址重写,例如:

<td>

    <a href="<%=response.encodeURL("index.jsp?c=1&wd=Java") %>">
    Homepage</a>

</td>

该方法会自动判断客户端是否支持Cookie。如果客户端支持Cookie,会将URL原封不动地输出来。
如果客户端不支持Cookie,则会将用户Session的id重写到URL中。重写后的输出可能是这样的:

<td>

    <ahref="index.jsp;jsessionid=0CCD096E7F8D97B0BE608AFDC3E1931E?c=
    1&wd=Java">Homepage</a>

</td>

即在文件名的后面,在URL参数的前面添加了字符串“;jsessionid=XXX”。其中XXX为Session的id。分析一下可以知道,增添的jsessionid字符串既不会影响请求的文件名,
也不会影响提交的地址栏参数。用户单击这个链接的时候会把Session的id通过URL提交到服务器上,服务器通过解析URL地址获得Session的id。
如果是页面重定向(Redirection),URL地址重写可以这样写:
<%
    if(“administrator”.equals(userName))
    {
       response.sendRedirect(response.encodeRedirectURL(“administrator.jsp”));
        return;
    }
%>

效果跟response.encodeURL(String url)是一样的:如果客户端支持Cookie,生成原URL地址,如果不支持Cookie,传回重写后的带有jsessionid字符串的地址。
对于WAP程序,由于大部分的手机浏览器都不支持Cookie,WAP程序都会采用URL地址重写来跟踪用户会话。比如用友集团的移动商街等。
注意:TOMCAT判断客户端浏览器是否支持Cookie的依据是请求中是否含有Cookie。尽管客户端可能会支持Cookie,
但是由于第一次请求时不会携带任何Cookie(因为并无任何Cookie可以携带),URL地址重写后的地址中仍然会带有jsessionid。
当第二次访问时服务器已经在浏览器中写入Cookie了,因此URL地址重写后的地址中就不会带有jsessionid了。

Session中禁止使用Cookie
既然WAP上大部分的客户浏览器都不支持Cookie,索性禁止Session使用Cookie,统一使用URL地址重写会更好一些。
Java Web规范支持通过配置的方式禁用Cookie。下面举例说一下怎样通过配置禁止使用Cookie。
打开项目sessionWeb的WebRoot目录下的META-INF文件夹(跟WEB-INF文件夹同级,如果没有则创建),打开context.xml(如果没有则创建),编辑内容如下:
代码1.11 /META-INF/context.xml
<?xml version='1.0' encoding='UTF-8'?>
<Context path="/sessionWeb"cookies="false">

</Context>

或者修改Tomcat全局的conf/context.xml,修改内容如下:
代码1.12  context.xml
<!-- The contents of this file will be loaded for eachweb application -->

<Context cookies="false">

    <!-- ... 中间代码略 -->

</ontext>

部署后TOMCAT便不会自动生成名JSESSIONID的Cookie,Session也不会以Cookie为识别标志,而仅仅以重写后的URL地址为识别标志了。
注意:该配置只是禁止Session使用Cookie作为识别标志,并不能阻止其他的Cookie读写。
也就是说服务器不会自动维护名为JSESSIONID的Cookie了,但是程序中仍然可以读写其他的Cookie。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值