HTTP Session

本文主要介绍了HTTP Session,因HTTP协议无法保存客户端状态信息,Web Server引入session概念。阐述了session工作方式、HTTP Request组成及实现Session的URL重写和Cookie方法。还给出相关权威资料,分析了Tomcat5的HTTP Session实现,最后提及在frameset中使用Session及相关概念。
HTTP Session
一、浅析

HTTP协议(http://www.w3.org/Protocols/)是“一次性单向”协议。

服务端不能主动连接客户端,只能被动等待并答复客户端请求。客户端连接服务端,发出一个HTTP Request,服务端处理请求,并且返回一个HTTP Response给客户端,本次HTTP Request-Response Cycle结束。

我们看到,HTTP协议本身并不能支持服务端保存客户端的状态信息。于是,Web Server中引入了session的概念,用来保存客户端的状态信息。

这里用一个形象的比喻来解释session的工作方式。假设Web Server是一个商场的存包处,HTTP Request是一个顾客,第一次来到存包处,管理员把顾客的物品存放在某一个柜子里面(这个柜子就相当于Session),然后把一个号码牌交给这个顾客,作为取包凭证(这个号码牌就是Session ID)。顾客(HTTP Request)下一次来的时候,就要把号码牌(Session ID)交给存包处(Web Server)的管理员。管理员根据号码牌(Session ID)找到相应的柜子(Session),根据顾客(HTTP Request)的请求,Web Server可以取出、更换、添加柜子(Session)中的物品,Web Server也可以让顾客(HTTP Request)的号码牌和号码牌对应的柜子(Session)失效。顾客(HTTP Request)的忘性很大,管理员在顾客回去的时候(HTTP Response)都要重新提醒顾客记住自己的号码牌(Session ID)。这样,顾客(HTTP Request)下次来的时候,就又带着号码牌回来了。

Session ID实际上是在客户端和服务端之间通过HTTP Request和HTTP Response传来传去的。号码牌(Session ID)必须包含在HTTP Request里面。关于HTTP Request的具体格式,请参见HTTP协议(http://www.w3.org/Protocols/)。这里只做一个简单的介绍。

在Java Web Server(即Servlet/JSP Server)中,Session ID用jsessionid表示(请参见Servlet规范)。

HTTP Request一般由3部分组成:

(1)Request Line

这一行由HTTP Method(如GET或POST)、URL、和HTTP版本号组成。

例如,GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1

GET http://www.google.com/search?q=Tomcat HTTP/1.1

POST http://www.google.com/search HTTP/1.1

GET http://www.somsite.com/menu.do;jsessionid=1001 HTTP/1.1



(2)Request Headers

这部分定义了一些重要的头部信息,如,浏览器的种类,语言,类型。Request Headers中还可以包括Cookie的定义。例如:

User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)

Accept-Language: en-us

Cookie: jsessionid=1001



(3)Message Body

如果HTTP Method是GET,那么Message Body为空。

如果HTTP Method是POST,说明这个HTTP Request是submit一个HTML Form的结果,

那么Message Body为HTML Form里面定义的Input属性。例如,

user=guest

password=guest

jsessionid=1001

主意,如果把HTML Form元素的Method属性改为GET。那么,Message Body为空,所有的Input属性都会加在URL的后面。你在浏览器的URL地址栏中会看到这些属性,类似于

http://www.somesite/login.do?user=guest&password=guest&jsessionid=1001



从理论上来说,这3个部分(Request URL,Cookie Header, Message Body)都可以用来存放Session ID。由于Message Body方法必须需要一个包含Session ID的HTML Form,所以这种方法不通用。

一般用来实现Session的方法有两种:

(1)URL重写。

Web Server在返回Response的时候,检查页面中所有的URL,包括所有的连接,和HTML Form的Action属性,在这些URL后面加上“;jsessionid=XXX”。

下一次,用户访问这个页面中的URL。jsessionid就会传回到Web Server。

(2)Cookie。

如果客户端支持Cookie,Web Server在返回Response的时候,在Response的Header部分,加入一个“set-cookie: jsessionid=XXXX”header属性,把jsessionid放在Cookie里传到客户端。

客户端会把Cookie存放在本地文件里,下一次访问Web Server的时候,再把Cookie的信息放到HTTP Request的“Cookie”header属性里面,这样jsessionid就随着HTTP Request返回给Web Server。
二、相关资料

前面是我自作聪明的一段个人浅见,下面我来找点权威资料支持。

http://www.w3.org/Protocols/



Use of HTTP State Management (RFC 2964).

ftp://ftp.rfc-editor.org/in-notes/rfc2964.txt



这个文件是定义“HTTP State Management”扩展协议部分。里面有这么一段,

It's important to realize that similar capabilities may also be achieved using the "bare" HTTP protocol, and/or dynamically-generated

HTML, without the State Management extensions. For example, state information can be transmitted from the service to the user by embedding a session identifier in one or more URLs which appear in HTTP redirects, or dynamically generated HTML; and the state information may be returned from the user to the service when such URLs appear in a GET or POST request. HTML forms can also be used to pass state information from the service to the user and back, without the user being aware of this happening.



这段话的意思是说,不用这个 “HTTP State Management”扩展协议部分,我们也可以用“纯粹”的HTTP协议实现Session -- 比如URL重写,HTML Form等。

这里面没有提到Cookie。因为“HTTP State Management” 扩展协议部分本身包括了关于Cookie的内容。这一点可以通过

HTTP State Management Mechanism (RFC 2965),

ftp://ftp.rfc-editor.org/in-notes/rfc2965.txt

看出来。



STATE AND SESSIONS

This document describes a way to create stateful sessions with HTTP requests and responses. Currently, HTTP servers respond to each client request without relating that request to previous or subsequent requests; the state management mechanism allows clients and servers that wish to exchange state information to place HTTP requests and responses within a larger context, which we term a "session". This context might be used to create, for example, a "shopping cart", in which user selections can be aggregated before purchase, or a magazine browsing system, in which a user's previous reading affects which offerings are presented.

Neither clients nor servers are required to support cookies. A server MAY refuse to provide content to a client that does not return the cookies it sends.



后面还给出了例子(其中的汉语部分是我加的)。



4.1 Example 1

Most detail of request and response headers has been omitted. Assume

the user agent has no stored cookies.



1. User Agent -> Server



POST /acme/login HTTP/1.1

[form data]



User identifies self via a form.



2. Server -> User Agent



HTTP/1.1 200 OK

Set-Cookie2: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"



Cookie reflects user's identity.

(这里面的 Customer="WILE_E_COYOTE" 应该就是从Form Data里面来的,这时候又传回给了客户端)



3. User Agent -> Server



POST /acme/pickitem HTTP/1.1

Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"

[form data]



User selects an item for "shopping basket".



4. Server -> User Agent



HTTP/1.1 200 OK

Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";

Path="/acme"



Shopping basket contains an item.

(这个火箭发射器显然也是从Form Data来的。但为什么Part_Number="Rocket_Launcher_0001"也需要传回给客户端?

Customer="WILE_E_COYOTE"; 应该是在传统的”Set-Cookie”里面传回给客户端的。” Set-Cookie2” 的作用应该是向Cookie里面添加东西。)



5. User Agent -> Server



POST /acme/shipping HTTP/1.1

Cookie: $Version="1";

Customer="WILE_E_COYOTE"; $Path="/acme";

Part_Number="Rocket_Launcher_0001"; $Path="/acme"

[form data]



User selects shipping method from form.



(客户传给服务器的Cookie里面包括了Customer和Part_Number)



6. Server -> User Agent



HTTP/1.1 200 OK

Set-Cookie2: Shipping="FedEx"; Version="1"; Path="/acme"



New cookie reflects shipping method.



7. User Agent -> Server



POST /acme/process HTTP/1.1

Cookie: $Version="1";

Customer="WILE_E_COYOTE"; $Path="/acme";

Part_Number="Rocket_Launcher_0001"; $Path="/acme";

Shipping="FedEx"; $Path="/acme"

[form data]





User chooses to process order.



8. Server -> User Agent



HTTP/1.1 200 OK



Transaction is complete.



The user agent makes a series of requests on the origin server, after each of which it receives a new cookie. All the cookies have the same Path attribute and (default) domain. Because the request-URIs all path-match /acme, the Path attribute of each cookie, each request contains all the cookies received so far.

(看到这里,我才大致明白,原来那个$Path="/acme" 大致起着 JSessionID的作用)


三、Tomcat5的HTTP Session实现

下面我们来看Tomcat5的源代码如何支持HTTP 1.1 Session。

我们可以用jsessionid, Set-Cookie等关键字搜索Tomcat5源代码。



首先,我们来看常量定义:

org.apache.catalina.Globals



/**

* The name of the cookie used to pass the session identifier back

* and forth with the client.

*/

public static final String SESSION_COOKIE_NAME = "JSESSIONID";





/**

* The name of the path parameter used to pass the session identifier

* back and forth with the client.

*/

public static final String SESSION_PARAMETER_NAME = "jsessionid";



Cookie里面用大写的JSESSIONID,URL后缀用的是小写的jsessionid。

Session的具体实现类是org.apache.catalina.session.StandardSession。一个Tomcat Server的所有Session都由一个Manager(拥有一个Context)统一管理。我估计有一个 Session Listener 专门管理Cluster之间的Session数据复制,具体的我没有追查下去。



另外几个重要的相关类是org.apache.coyote.tomcat5包下面的CoyoteRequest , CoyoteResponse, CoyoteAdapter三个类。



org.apache.coyote.tomcat5.CoyoteResponse类的toEncoded()方法支持URL重写。

String toEncoded(String url, String sessionId) {



StringBuffer sb = new StringBuffer(path);

if( sb.length() > 0 ) { // jsessionid can't be first.

sb.append(";jsessionid=");

sb.append(sessionId);

}

sb.append(anchor);

sb.append(query);

return (sb.toString());

}



我们来看org.apache.coyote.tomcat5.CoyoteRequest的两个方法configureSessionCookie()

doGetSession()用Cookie支持jsessionid.



/**

* Configures the given JSESSIONID cookie.

*

* @param cookie The JSESSIONID cookie to be configured

*/

protected void configureSessionCookie(Cookie cookie) {



}



HttpSession doGetSession(boolean create){



// Creating a new session cookie based on that session

if ((session != null) && (getContext() != null)

&& getContext().getCookies()) {

Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,

session.getId());

configureSessionCookie(cookie);

((HttpServletResponse) response).addCookie(cookie);

}



}


四、More

HTTP Session的协议、规范、实现大概就是这些。

另外,在frameset中使用Session的情况有些复杂,不同环境表现可能不同。

其余相关的概念有“单点登录”(Sing Sign On – SSO), Domain, Cluster, Proxy, Cache等。
<think>好的,用户现在想了解HTTP Session相关的技术信息或解决方案。之前用户提供的引用内容主要涉及Session在不同技术栈中的应用,比如.NET中的Session服务器配置、C#与Redis的分布式Session、Koa.js的generic-session,以及基于神经网络的会话推荐系统。用户之前的问题是关于HTTP协议的工作原理,现在转向Session管理,可能是在实际开发中遇到了状态保持或分布式环境下的Session共享问题。 首先,我需要确认用户的需求。用户可能想知道如何在不同场景下有效使用HTTP Session,特别是分布式系统中的Session共享问题,以及如何解决Session管理中的常见问题,如安全性、性能等。引用中提到了Session服务器、Redis、以及不同框架的配置方法,这些都是关键点。 接下来,我需要整理HTTP Session的基本概念,解释其作用,比如保持用户状态、跟踪会话。然后,针对分布式系统,需要说明Session共享的必要性,比如负载均衡时如何确保不同服务器能访问同一Session数据。这里可以结合引用中的.NET Session服务器和Redis方案。 还要注意用户提到的不同技术栈,比如Koa.js的配置示例,可能需要补充其他常见框架的处理方法,比如Spring Session,以覆盖更广泛的开发者群体。同时,引用中的安全问题,比如Session劫持,需要给出防范措施,如使用HTTPS、设置HttpOnly和Secure标志。 另外,用户可能对性能优化感兴趣,比如Session存储的选择(内存、数据库、缓存系统),需要比较优缺点,帮助用户根据场景选择合适方案。例如,内存存储速度快但不适合分布式,Redis适合高并发和分布式环境。 最后,结合用户提供的引用中的代码示例,可能需要进一步解释这些配置的具体步骤,比如在Koa中如何配置generic-session,或者在C#中如何初始化Redis Session。同时,引用提到的Session_End事件在ASP.NET中的限制,需要提醒用户注意在分布式环境下此类事件的不可靠性,建议采用其他机制替代。 总结来说,回答需要涵盖HTTP Session的基础、分布式处理方案、安全措施、性能优化、常见问题及解决方案,并结合用户提供的引用中的技术点,给出跨平台的实现示例,确保信息全面且实用。</think>### HTTP Session技术详解与解决方案 #### 一、Session基础概念 HTTP Session是用于**跨请求保持用户状态**的服务器端技术。当客户端首次访问服务器时,服务器会创建唯一Session ID(通常通过Cookie传递),并通过该ID关联存储在服务器的用户数据[^2]。例如: $$ \text{Session流程:} \quad \text{客户端} \xrightarrow{\text{携带Session ID}} \text{服务器} \xrightarrow{\text{读写Session存储}} $$ #### 二、分布式Session解决方案 在负载均衡场景中,需解决多服务器间的Session共享问题: 1. **Redis集中存储**(推荐方案) - 使用内存数据库Redis存储Session,支持高并发访问 - **C#实现示例**(引用[3]扩展): ```csharp public class RedisSessionConfig : HttpSessionStateStoreProviderBase { private ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379"); public override void SetItem(string id, string item, int timeout, bool lockRecord) { redis.GetDatabase().StringSet($"session:{id}", item, TimeSpan.FromMinutes(timeout)); } } ``` - 优点:读写速度达到$10^5$次/秒,支持数据持久化[^3] 2. **Session粘滞(Sticky Session)** - 通过负载均衡器将同一用户请求固定到特定服务器 - 缺点:故障转移时会导致Session丢失 3. **数据库存储** - 将Session数据存入MySQL等关系型数据库 - 适用场景:数据需要长期保存且访问频率较低 #### 三、安全防护措施 1. **Session劫持防御** - 强制使用HTTPS传输Session ID[^1] - 设置Cookie属性:`HttpOnly`、`Secure`、`SameSite=Strict` 2. **Session固定攻击防护** ```javascript // Koa.js会话重置示例(引用[4]扩展) app.use(async (ctx, next) => { await next(); if (ctx.path === '/login') { const oldSessionId = ctx.sessionId; ctx.session.regenerate(); // 生成新Session ID await ctx.sessionStore.destroy(oldSessionId); } }); ``` #### 四、性能优化指南 | 存储方式 | 读写延迟 | 扩展性 | 数据一致性 | |------------|----------|--------|------------| | 内存 | <1ms | 差 | 弱 | | Redis | 1-5ms | 强 | 强 | | 数据库 | 10-50ms | 中 | 强 | **优化建议**: - 设置合理过期时间(通常$15\text{min} \leq TTL \leq 24\text{h}$) - 启用Session压缩(GZIP可减少$70\%$存储空间) - 避免存储大对象(建议单Session < 1MB) #### 五、跨平台实现示例 1. **Node.js (Koa)** ```javascript const redisStore = require('koa-redis')({ host: 'redis-server', port: 6379, ttl: 3600 // 单位:秒 }); app.use(session({ store: redisStore, key: 'SESSION_ID', rolling: true // 每次访问自动续期 })); ``` 2. **Java (Spring Session)** ```java @Configuration @EnableRedisHttpSession public class SessionConfig { @Bean public LettuceConnectionFactory connectionFactory() { return new LettuceConnectionFactory( new RedisStandaloneConfiguration("redis-server", 6379)); } } ``` #### 六、常见问题解决方案 1. **Session丢失问题** - 检查Cookie域设置:`domain=.example.com` - 验证服务器时间同步(NTP误差应 < 1s) - 排查负载均衡配置(需透传X-Forwarded-For) 2. **高并发场景优化** - 采用`Lua脚本`实现原子操作: ```lua -- Redis原子化续期脚本 if redis.call('EXISTS', KEYS[1]) == 1 then return redis.call('EXPIRE', KEYS[1], ARGV[1]) end return 0 ``` 3. **分布式锁问题**(引用[2]扩展) - 使用Redlock算法实现跨Redis节点锁 - 设置锁超时时间:$锁超时时间 \geq 业务最大执行时间 \times 3$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值