简单轻松实现单点登录(sso)

简单轻松实现单点登录(sso)_第1张图片

前言:

  • 本文虽然使用PHP语言讲单点登录,但是更多讲的是一种实现思想,不仅限于PHP。
  • 该单点登录方案完全实现跨域名。
  • JWT的使用只是生成一个唯一token字符串,也可以不利用JWT。
  • 重要存储Token方式,Cookie与Redis。
  • 支持原创文档。        
  • 该单点登录方案已稳定运用到本人所在公司的项目。
  • 欢迎讨论和提问。

一、WEB程序的“会话机制”

简单回顾:

1.http协议是无状态的

        是指协议对于交互性场景没有记忆能力,每次请求http都会当做一次新的请求。

2.让服务器有记忆能力之Cookie、Session

        WEB到底是如何采用Cookie+Session来进行“会话状态”维护的:

  •  浏览器发起第一次请求,服务器(php)创建一个唯一sessionID
  •  把sessionID设置到cookie中
  •  浏览器发送第二次、第N次请求都会携带cookie中的sessionID,服务器去识别是谁在请求简单轻松实现单点登录(sso)_第2张图片 

3.客户端禁用Cookie之后如何使用Session

        session是基于cookie的,但cookie只是传递一个sessionID,cookie禁用后我们可以利用url传sessionID的方式进行使用session。

4.举例:

        4.1 创建一个session并且,生成几个跳转其他页面的链接

简单轻松实现单点登录(sso)_第3张图片

        4.2 点链接后跳转的页面,输出session值

简单轻松实现单点登录(sso)_第4张图片

        4.3 演示结果:

简单轻松实现单点登录(sso)_第5张图片简单轻松实现单点登录(sso)_第6张图片

        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文件地址

简单轻松实现单点登录(sso)_第7张图片

二、单系统登录

        我们了解了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.a.com

admin.a.com

test.a.com

www.b.com

简单轻松实现单点登录(sso)_第8张图片  

三、单点登录

        1.解决什么问题

                随着公司业务扩展,肯定不止一个系统,如果每个系统都有一套登录逻辑,一个个去登录,难免会很麻烦,单点登录来解决这一问题。

        2.使用SSO的好处

                方便用户:用户使用系统时,一次登录,就有权访问多个网站,同时也不用牢记多套名称和密码。

                方便管理员:管理员只需要维护一套统一的用户账号。

                简化应用开发:开发新的业务系统时,登录逻辑可以直接对接sso的单点登录,简化开发流程。

        如下图,每个系统没有独立的登录页面,而是去sso系统登录,如果其中一个系统登录,那么其他系统会自动登录。

简单轻松实现单点登录(sso)_第9张图片

        3.实现思路

                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进行比较,校验通过完成登录。

        流程图:

简单轻松实现单点登录(sso)_第10张图片

                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失效则跳往登录页,完成注销)

图解:

        登录成功时记录:

简单轻松实现单点登录(sso)_第11张图片

        注销登录逻辑:

简单轻松实现单点登录(sso)_第12张图片

很感谢你耐心看到最后,如果感兴趣或者有任何问题,欢迎你的留言,作者会一一作答。

你可能感兴趣的:(PHP,php)