是指协议对于交互性场景没有记忆能力,每次请求http都会当做一次新的请求。
WEB到底是如何采用Cookie+Session来进行“会话状态”维护的:
session是基于cookie的,但cookie只是传递一个sessionID,cookie禁用后我们可以利用url传sessionID的方式进行使用session。
4.1 创建一个session并且,生成几个跳转其他页面的链接
4.2 点链接后跳转的页面,输出session值
4.3 演示结果:
4.4 要实现禁用cookie,地址栏自动携带sessionID需要改php.ini的相关配置:
在php.ini文件中
1.session.use_trans_sid=1 当浏览器禁止cookie时,页面上的链接会基于url传递SESSIONID
2.session.use_only_cookies=0 表示是否只开启基于cookie的session的会话方式(为1,禁用cookie后,无法使用session)
3.session.use_cookies=1 表示是否开启基于cookie的session会话(如果为0,地址栏有sessionID会默认取地址栏,不优先取cookie中的sessionID)
说明:
当前配置,如果浏览器禁用cookie则会取地址栏的PHPSESSID,浏览器没有禁用cookie则会取cookie中的PHPSESSID。
4.4 为什么跨域名还能访问同一个session?
因为我是在自己本地运行的,使用同一个PHP服务。
php.ini设置了保存session文件地址
我们了解了Cookie和Session机制,就可以利用它们做单系统登录。
//账号密码校验成功 在cookie中生成登录标识is_login
setcookie('is_login', true, time() + 1000, '/', 'qq.com');
//浏览器再次访问
if(isset($_COOKIE['is_login']) && $_COOKIE['is_login']) {
echo '已经登录';
} else {
echo '未登录';
}
缺点,浏览器出于对Cookie的保护,Cookie无法跨域获取,只能在同源域名下访问。
网址 |
是否同享cookie |
|
✅ |
|
✅ |
|
✅ |
www.b.com |
❎ |
随着公司业务扩展,肯定不止一个系统,如果每个系统都有一套登录逻辑,一个个去登录,难免会很麻烦,单点登录来解决这一问题。
方便用户:用户使用系统时,一次登录,就有权访问多个网站,同时也不用牢记多套名称和密码。
方便管理员:管理员只需要维护一套统一的用户账号。
简化应用开发:开发新的业务系统时,登录逻辑可以直接对接sso的单点登录,简化开发流程。
如下图,每个系统没有独立的登录页面,而是去sso系统登录,如果其中一个系统登录,那么其他系统会自动登录。
3.1 A系统
1. 用户访问系统A,系统A发现用户未登录(中间件校验局部token是否有效),跳转至sso系统校验全局是否登录,并将自己的地址等作为参数。
2. sso系统发现用户未登录,跳转到sso登录页面。
3. 用户输入账号密码提交登录。
-----------------------ajax请求sso登录接口开始--------------------------------------------------
4. sso登录接口,从数据库验证账号密码是否正确。
5. 拿到用户ID,传入JWT生成加密token。
6. 建立全局会话(把token保存到redis、cookie中,并设置有效期 )
$timeout = 3600;//有效时间为1小时
//1.{token:token}格式保存到redis 用于中间件中校验 cookie中的token是否与redis一致
$redis->setex('redis中的全局token' . $token, $timeout, $token);
//2.保存到cookie中 有效期为24小时
setCookies('cookie中的全局token', $token, time() + 3600 * 24, '/');
7. 建立局部会话(curl方式请求A系统创建局部token的方法,并返回局部token)
$url = "http://www.A系统.com/receiveToken";
$params = ['token' => 全局token, 'user_id' => 用户id];
A系统局部token = $this->curlPost($url, $params);
8. sso系统带着局部token,返回ajax请求,js跳转到A系统。
location.href = 'http://www.A系统.com?local_token=A系统局部token';
-----------------------ajax请求sso登录接口结束---------------------------------------------------
9. A系统:
接收地址栏局部local_token 设置cookie中,判断是否登录的中间件中会用cookie中的局部token与redis中的局部token进行比较,校验通过完成登录。
流程图:
3.2 B系统
1. 访问B系统,中间件中会判断cookie中的局部token是否有效(1.是否存在,2.是否与redis中一致)。
2. 局部token无效,则带着B系统回调地址,跳往sso验证全局token的方法。
3. sso系统验证全局token的方法中,校验全局cookie中的token是否有效(1.是否存在,2.是否与redis中的一致)。
4. 全局token有效,则重新生成全局token(设置cookie,redis),并给B系统分配局部token(curl方式请求B系统创建局部token的方法,并返回局部token)。
5. 重定向跳往B系统的回调地址,地址栏并携带局部token。
6. B系统接收sso系统传来的局部token,设置cookie中。
7.中间件校验是否登录,会用cookie中的局部token与redis进行校验。(第4步时已经把局部token保存在B系统的redis中),校验通过完成登录。
说明:
第4步,全局token有效,但还是重新生成全局token,是等同于重新登录,重新设置过期时间等。
3.3 延长登录有效期
需求场景:
登录成功后,例如登录状态有效期为2小时,如果过了2小时一直没有刷新浏览器(没有新的请求)则登录状态失效,如果中途有访问,则自动延长登录有效期。
实现方案:
0.前提:登录把sso全局token的cookie设置24小时有效期或不设置有效时间。
1.子系统中间件中,判断局部token在redis中的登录有效期时间,如果小于30分钟,则 curl方式调用SSO系统延长有效期接口。
2.SSO系统延长redis中全局token有效期。
3.之后延长子系统局部redis中token有效期。
说明:
问题:为什么cookie中的有效期与redis中的不一致?
答:因为为了更好的用户体验,我们只是通过curl方式去延长有效期,不用管cookie,对于用户是无感知的。因为cookie是基于浏览器的,如果需要延长cookie有效期,还得浏览器去跳转。
注销一个系统,同时也注销其他已经登录的系统。
思路:
1.登录SSO、或验证全局登录成功后,给子系统分配局部token时创建redis集合,redis的key为subsystem_url_token_list_+全局token ,值为局部子系统地址及局部token。
$子系统url及子系统局部token = [
['callback_url' => 'http://www.A系统.com', 'local_token' => 局部token],
['callback_url' => 'http://www.B系统.com', 'local_token' => 局部token],
];
2.点击注销登录,后端删除当前系统cookie中的token,然后重定向至sso系统退出登录的方法,方法中从cookie中获取全局token,然后从redis集合中获取所有子系统的url和local_token,循环 根据url去各自子系统接收注销的方法,子系统注销登录的方法中会根据local_token删掉子系统redis中的局部token,全部执行成功,再删掉全局redis中的token及cookie中的token。成功后 跳往登录页。(所有子系统中间件会校验cookie中的token是否与redis一致,不一致去校验全局token,全局token失效则跳往登录页,完成注销)
图解:
登录成功时记录:
注销登录逻辑: