优雅地使用CodeIgniter 3之Session类库(1)(转)
相信无数人在使用CI2的Session类库时,遇到各种的坑,各种抱怨,各种不解。在CI中国论坛能搜到大量关于Session类库的提问,说明要想用 好session类库还是得下一番功夫。本文将先从CI2入手,讲述CI下Session类库的设计理念和在CI3下的Session类库做了哪些重大改 进。首先在这里简单说明一下Session和cookie的区别。
Session和cookie的区别
1.在某些语境中,cookie是session的一种实现方式,Ci的类库设计似乎就这么认为的。设想用户登录后使用Session保存数据的一种情 景。用户输入密码和口令发送到服务器后,php就会在服务器端生成一个php sessionid和一个具体的数据包放到一个文件中,并存放到指定的文件夹下(如果session.save_handler=files),然后会把 sessionId作为一个cookie放到响应中返回给客户端(前提是用户浏览器开启了cookie,否则会加入到url地址的查询字符串中)。用户再 次访问服务器上的其他页面时,会把这个phpsessionid的cookie携带上作为请求的一部分发送给服务器,服务器就能根据session id到保存session的文件夹下找到特定的文件,读取其中的数据。
2.而cookie,在服务器端和客户端都是可以设置的。在客户端设置(通过js)时,主要作用是用户在自己的浏览器页面跳转时能传递数据。服务端不会太
多理睬客户端设置的cookie,客户端发过来什么,它就原封不动地返回什么。同时,服务器也会发出让客户端设置cookie的指令,下次请求时客户端也
会无脑地把cookie发送到服务器。
在CI2.0版本的Session类库中,默认使用了cookie来保存session,便带给我们各种不解。下面谈下CI2.0的session工作原理。
基本原理
使用Session库时,会有一个元数据。我们可以把这个元数据理解为在浏览器和服务器之间的一个令牌,有了这个令牌,服务器的城门才会对客户端的请求敞 开。客户端好比是一个信使,要去服务器的王宫里报告信息,服务器的门卫就会检查这个令牌,这个令牌包含的基本信息是:ip地址、user agent、上次活动时间、Session id,其中Session id是重要的,Session id不对,直接拒绝。但是门卫也可能根据上级指示,需要根据他的外表判断是否是来自特定地区(IP地址的限制)、看下他的服饰是否符合某些特征(user agent限制),是否令牌是否已经过期(上次活动时间),如果都满足才能让特定的西域人进入王宫。
1 |
Array |
2 |
( |
3 |
[session_id] => 4a5a5dca22728fb0a84364eeb405b601 |
4 |
[ip_address] => 127.0.0.1 |
5 |
[user_agent] => Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; |
6 |
[last_activity] => 1303142623 |
7 |
) |
门卫得到的上级指示来自于配置文件,它们放在config.php中:
1 |
$config [ 'sess_match_ip' ] = FALSE; |
2 |
$config [ 'sess_match_useragent' ] = TRUE; |
3 |
$config [ 'sess_time_to_update' ] = 300; |
门卫会拿令牌里的信息(http请求携带的cookie)和真实的信息(http请求头的信息)比对,判断你是否是合法的,不合法的话,就会认为你来路不 明,拒绝你携带来的任何数据信息。如果你在使用CI自带的Session库时,总是出现读不到数据的情况,你就要看下你是否要求门卫检查的过于严格。特别 是当设置sess_match_useragent设为TRUE时,会遇到各种的坑:
1. 你用flash组件上传文件,只有登录的用户才能上传文件,结果你每次判断用户是否登录都会出错,因为flash发送的http请求有可能更改了user agent;
2. 使用ie切换不同的模式,比如兼容模式,也会造成user agent不同;
3. user agent的长度最长是120个字符,手动设置User agent是需要截取字符到最大120个。
4. 另外,如果出现“Cannot modify header information - headers already sent by”错误,基本可以断定是你文件的编码格式有误,请去掉bom头。
安全问题
CI使用cookie来传递数据本来就不够安全,而且如果数据量巨大时也会有性能问题。但是CI还是友好地加入以下几个安全验证机制:
1. 加密令牌
1 |
$config [ 'encryption_key' ] = 'mahuaz_' ; |
2 |
$config [ 'sess_encrypt_cookie' ] = TRUE; |
设置后,客户端检查cookie时就可以看到加密后的序列化数据。
2. 自动更新机制
CI
默认每五分钟更新一次令牌,更新发生在客户端的一次请求中。客户端每发送一次请求,会把cookie的信息发送到服务器,服务器根据发来的cookie判
断是否到了应该更换令牌的时间了,如果是,就会重新换一个新的令牌返回给客户端。这就相当于门卫给你换了令牌,下次要使用新令牌进门。此时即使有坏人伪造
了一个令牌也不起作用了,因为旧令牌已经作废。这样就相当于加了一条安全机制。可以使用:
1 |
$config [ 'sess_time_to_update' ] = 300; |
来设置多长时间来换一次令牌。这个时间不要设置的太短,更新频繁也会影响性能。这里不要和sess_expiration混淆:
1 |
$config [ 'sess_expiration' ] = 7200; |
这个配置是用来指示整个cookie的过期时间的,相当于令牌完全失效,再怎么更换都不起作用。
在CI中文论坛上,每每有遇到各种session问题,总有人推荐使用hex封装的Session库 ,或者使用stblog的作者Saturn的Session库 https://github.com/cnsaturn/codeigniter-my-session前者使用PHP原生Session,不需要额外配置;后者需要安装 memcached 服务以及 memcache/memcached 扩展。hex封装的这个Session库特性有限,它有如下特点:
1. 简化了CI自带Session的安全机制,没有做每五分钟更新session_id的设置;
2.没有加密Session;
3.永远不会过期,虽然它会读取$config['sess_expiration'] = 7200;判断Session是否过期,但是如果发现过期的话,并不是直接返回空数据,而是重新产生一个新的Session id,把旧数据转到新数据中。除非你超过了最大gc回收时间:7200s。
4.config.php中的以下配置节都没任何用处:
1 |
$config [ 'sess_cookie_name' ] = 'ci_session' ; |
2 |
//$config['sess_expiration'] = 7200; 该配置节有用 |
3 |
$config [ 'sess_expire_on_close' ] = FALSE; |
4 |
$config [ 'sess_encrypt_cookie' ] = FALSE; |
5 |
$config [ 'sess_use_database' ] = FALSE; |
6 |
$config [ 'sess_table_name' ] = 'ci_sessions' ; |
7 |
$config [ 'sess_match_ip' ] = FALSE; |
8 |
$config [ 'sess_match_useragent' ] = TRUE; |
9 |
$config [ 'sess_time_to_update' ] = 300; |
所以,使用这个库也只能解决大部分问题,真正的解决方案也只能等到CI3来处理,事实上CI3也如愿地解决了上面提到的很多问题。下一节我们讲讲如何使用CI3的session。
http://www.ifixedbug.com/posts/how-to-use-codeigniter3-session-1