PHP 之 SESSION 详解
Posted 二环狄仁杰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP 之 SESSION 详解相关的知识,希望对你有一定的参考价值。
session 的本质
本质就是在服务器上保存数据,然后给客户端一把钥匙,客户端通过钥匙就能拿到这些数据,这就是 session
。
数据能保存在哪里? php
默认保存在服务器文件里,也就是写文件读文件。
会话工作流程
写
session
:当服务器调用session
相关函数设置session
时,被操作的数据会保存在磁盘上,并生成一个session_id
,在http
的响应头里把这个session_id
发给浏览器。读
session
:浏览器发起http
请求,请求头中携带session_id
,服务器获取到这个session_id
,从磁盘上把对应数据读出来。
一般情况下 session_id
都是通过 cookie
下发给浏览器的,而浏览器发起请求时会自动携带 cookie
,所以用户几乎不用亲自操作 session_id
。
基础概念
显式执行了
session_start()
或隐式开启了session(session.auto_start=1
),就会生成会话文件(session文件),没有开启就不会生成。会话id可以用存在客户端
cookie
,也可以用url
参数传递,客户端通过会话id获取服务器的的会话数据。任何客户端拿到会话id,都可以访问服务器的会话数据。
每一个会话都会创建一个
session
文件,如果把存session_id
的cookie
删掉,再访问又会重新创建一个session文件unset($_SESSION['3'])
只会删除文件内对应的数据,而session_destory()
会删除sesssion文件,php结束时,会话会关闭;也可以手动调用
session_write_close()
来关闭会话开始会话后,php会给会话文件加锁,直到会话关闭,其他脚本才能再访问,高并发网站会引起其他页面阻塞,因此需要尽早调用sessionwriteclose()来释放文件锁,或者换能支持并发的session处理器(
session.save_handler
),比如存redissession(和所有已注册的变量)将被php内置的序列化方法在请求完成时序列化再保存,正因为会序列化,所以session不能存resource资源,也不能保存引用
session内容会保持key,value值,value类型
数据序列化方式
session.serialize_handler
默认值是php
,最好改为php_serialize
,
原因是:数字索引或字符串索引包含了(|和!)不能被使用,不能写入session文件保存,也会导致脚本执行关闭时出现错误。
以下都将无法保存,除非使用php_serialize
$_SESSION[2] = 2;
$_SESSION['3'] = '3';
$_SESSION['a|'] = '3';
$_SESSION['a!'] = '3';
过期的session,只要没有被删除,还是可以再访问到的。
session配置项
以下统一省略了 session
前缀
.save_handler = files // 存储方式
.save_path = '/tmp' // 存储目录;也可以='N;/path' ='N;MODE;/path'
N是目录深度,要用N必须先创建好目录,session自己不会创建目录;同时如果用了N并且N>0,自动回收就会失效
MODE是权限,默认创建的文件权限是600
.name = PHPSESSID // 会话名,用cookie存session_id时cookie的key,只能由数字或字母组成
.auto_start = 0 // 是否在请求开始时自动启动一个会话
.serialize_handler = php // 序列化反序列化时用的处理器
.gc_probalility = 1 //
.gc_divisor = 100 // 和上一个参数合起来用,此时表示1%,请求100次可以触发一次垃圾回收机制
.gc_maxlifetime = 1440 // 1440秒没有访问,就会被视为垃圾,如果设置了多级目录保存,可以手动清除session文件
find /path/to/sessions -cmin +24 -type f | xargs rm
.referer_check = '你的网址' // 使用.use_trans_sid 时建议开启这个,只接受这个网址跳转的请求来获取会话数据;注意使用HTTPS , 浏览器将不会发送 referrer 请求头。
.use_strict_mode = 0 // 开启时,会话模块只接受由它自己创建的会话id,不接受浏览器传递过来的未初始化的session_id;如果浏览器传来了,就返回一个新session_id; 默认关闭,浏览器传了未初始化的,就直接用那个未初始化的session_id创建一个session文件。
.use_cookies = 1 // 客户端使用cookie来存放session_id
.use_only_cookies = 1 // 必须使用cookie来存放session_id,防止URL方式的攻击
.cookie_lifetime = 0 // 保存session_id的cookie的生命周期,0是浏览器关闭就失效;该时间是相对于服务器的时间,和客户端浏览器可能不一样
.cookie_path='/' // cookie保存的路径,默认是/
.cookie_domain // cookie生效的域名,默认不设置
.cookie_secure=1 // 是否只通过安全连接发送,在HTTPS时才生效
.cookie_httponly=1 // 是否只能通过http协议来访问,开启后可以杜绝js来操作cookie,有效防止XSS攻击(不是所有浏览器都有效)
.hash_function=0 // 生成session_id所用的算法,0表示md5(128位),1表示sha1(160位);从5.3开始可以使用更多的算法,查看hash_algos()方法的返回值可以获取所有可用的算法,这个参数除了 是int也可以是string
.hash_bits_per_character=5 // 允许用户定义将二进制散列数据转换为可读的格式时每个字符存放多少个比特。可能值为 '4'(0-9,a-f),'5'(0-9,a-v),以及 '6'(0-9,a-z,A-Z,"-",",")。至于是干啥的没搞懂。
.use_trans_sid=0 // 是否启用URL传递session_id,如果用户cookie禁用,就自动在URL里传递;默认关闭
.url_rewriter.tags="a=href,area=href,frame=src,input=src,form=fakeentry" // 启用URL传递时才生效,可以不理。指定在使用透明 SID 支持时哪些 html 标记会被修改以加入会话 ID
.cache_limiter=nocache 共有none/nocache/private/private_no_expire/public这些值可选,这和header控制那个页面缓存时间是一样的,一般不使用,[想看更多](http://php.net/manual/zh/function.session-cache-limiter.php)
.cache_expire=180 缓存时间秒数
.upload_progress.enabled=1/0 // 是否开启文件上传进度追踪,开启后
.upload_progress.cleanup=1/0 // 上传完毕后清理session
.upload_progress.prefix='' // 设置key的前缀,会被包含在.upload_progress.name里;默认是"upload_progress_".
.upload_progress.name='' // 和前缀一起使用,默认是"PHP_SESSION_UPLOAD_PROGRESS"。完整的key是$_SESSION[两个拼接起来]
.upload_progress.freq=1% // 多久更新一次上传进度,可以是100字节,也可以是1%(默认值)
.upload_progress.min_freq=1 // 两次进度更新的间隔,默认1秒
.lazy_write=1 // php7新特性,开启后只有在session有变化时才写入
更详细的看: http://php.net/manual/zh/session.upload-progress.php
服务器对session的删除机制:
每一次请求会有一定的概率触发session回收机制,概率是session.gcprobability=1 除以 session.gcdivisor = 100。上面两个配置算的概率是1%,意思是每一次会话请求,会有1%的概率触发回收,删除所有不活跃时间达到session.gcmaxlifetime设置时间的session文件。不活跃时间用文件的最后修改时间计算。另外:如果使用session.savepath设置session文件保存在其他地方,那么这个删除机制可能不会有效,就需要自己定时去删除了。
另外:一台服务器如果有两个应用A和B,A应用设置session.gcmaxlifetime为10分钟,B应用设置为5分钟,那么5分钟到了就会把A的session也过期了,因为两个应用的session目录都是一个;解决这个问题就需要session.savepath把各自的应用设置到各自的目录里。
读取已过期的session会发生什么?
由于原理是先从session文件里读数据,然后有一定概率触发回收机制,然后再更新最后访问时间。所以读取已经过期的session文件会发生:1,被读取到,触发了回收机制把它删除,然后就读不到了。2,被读取到,没有触发回收机制,更新了它的最后访问时间,它又回到有效期了。
设置一个30分钟就过期的session:
根据上面的内容可以看出,默认情况下,其实我们无法严格意义上的设置一个指定时间就不能访问的session,但在一般应用中,勉强可以用下面的方式来实现一个并不严格的:0,session.usecookies=1 // 用cookie来存会话id 1,session.cookielifetime = 1800 // sessionid在客户端只保存30分钟, 2,session.gcmaxlifetime = 1800 // session文件在服务器端只保存30分钟。
如果一定要严格意思的设置过期,可以在代码里自己为每一个session值增加一个时间戳,每次访问前,判断时间戳。或者用redis等其他方式来存储session数据,redis的数据到期之后就不能再get到。
session安全
为了尽可能提高session的安全性,可以做如下设置:
session.cookie_lifetime=0; // 浏览器关闭会话id就销毁
session.use_cookies=on; // 用cookie传递会话id
session.use_only_cookies=on; // **只用cookie**传递会话id而不是url
session.use_strict_mode=On; // 让会话管理器只接受由自己创建的会话id
session.cookie_httponly=on; // 禁止 javascript 访问会话 cookie
session.cookie_secure=On; // 只允许在https协议下访问cookie存的session_id
session.hash_function = "sha256" // 生成更高强度的session_id
session函数
查看全部session函数: http://php.net/manual/zh/book.session.php
以上是关于PHP 之 SESSION 详解的主要内容,如果未能解决你的问题,请参考以下文章