基于 CAS 实现通用的单点登录解决方案(三):用户单点退出实现


前面两篇教程学院君陆续介绍了 CAS 单点登录实现原理,CAS Server 及 Client 环境搭建和单点登录流程演示,这篇教程我们简单来介绍下 CAS 单点退出实现。

原理概述

基于 CAS 单点登录系统的退出主要包含两个部分:

  • CAS 客户端应用的退出
  • CAS 服务端退出登录状态

我们在 CAS 客户端应用退出后将页面重定向到服务端退出登录状态,然后通过传递过去的回跳地址跳转回客户端应用退出后跳转页面。当然,此时通过单点登录的其它客户端系统还是处于登录状态的,需要等到对应 Session 过期后才能退出,因为我们使用的 CAS 扩展包是基于 PHP 自带的 Session 实现的登录操作,生成的用于存储 Session ID 的 Cookie 完全是随机的,跟指定用户没有任何关联,所以无法通过后端代码实现指定用户前端 Cookie 和后端 Session 的清空。

客户端配置

介绍了大致原理后,实现单点退出很方便,无论是 CAS 客户端还是服务端扩展包都已经在底层为我们封装好了退出实现逻辑,我们只需要稍加配置和定义退出路由即可。

首先我们来看客户端配置,以 testapp 为例,在 .env 中新增 CAS 服务端退出地址及退出后回跳客户端地址配置项:

CAS_LOGOUT_URL=https://blog56.test/cas/logout
CAS_LOGOUT_REDIRECT=http://app.test

然后在 routes/web.php 中配置客户端退出路由:

Route::middleware('cas.auth')->get('/logout', function () {
    cas()->logout();
});

这样,就完事了,具体的实现代码通过 cas()->logout() 执行,对应的底层实现位于 Subfission\Cas\CasManager 类的 logout 方法中,感兴趣的同学可以看下,其实就是我们上面介绍的退出原理,清空本地 Session,实现本地退出,然后根据前面配置的地址重定向到 CAS 服务端退出地址,并带上回跳地址参数。

服务端配置

服务端不需要任何额外配置,退出路由 leo108/laravel_cas_server 扩展包也为我们提供好了:

Route::get('logout', 'SecurityController@logout')->name($p.'logout')->middleware($auth);

注:CAS 相关路由定义代码位于 vendor/leo108/laravel_cas_server/src/Http/routes.php 文件中。

相应的服务端退出实现代码位于 Leo108\CAS\Http\Controllers\SecurityController

public function logout(Request $request)
{
    $user = $this->loginInteraction->getCurrentUser($request);
    if ($user) {
        $this->loginInteraction->logout($request);
        $this->pgTicketRepository->invalidTicketByUser($user);
        event(new CasUserLogoutEvent($request, $user));
    }
    $service = $request->get('service');
    if ($service && $this->serviceRepository->isUrlValid($service)) {
        return redirect($service);
    }

    return $this->loginInteraction->showLoggedOut($request);
}

接下来,我们需要在前面自定义的服务类 App\Services\CAS\UserLogin 中新增一个 logout 方法实现:

public function logout(Request $request)
{
    $this->guard()->logout();
    $request->session()->invalidate();
}

这段代码会在上述控制器方法这一行代码执行:

$this->loginInteraction->logout($request);

然后系统会判断如果是通过代理授权的 Ticket 的话会清除对应的数据库记录,这样,就完成了服务端的退出逻辑,系统会触发 CasUserLogoutEvent 事件,如果你需要在服务端退出时干点什么的话可以监听这个事件并进行处理。

接下来,系统会从退出 URL 中解析 service 字段,该字段存放的是客户端回跳地址,我们校验这个地址是否有效(域名是否在 cas_service_hosts 中是否注册过),如果有效的话就会跳转会去。

如果回跳地址域名无效,或者客户端应用没有配置回跳地址,则通过 App\Services\CAS\UserLogin 类中的 showLoggedOut 方法进行处理,你可以在该方法中重定向到指定页面或者直接渲染退出视图。

单点退出演示

介绍完客户端和服务端配置和退出原理后,我们来简单演示下单点登录退出流程。

打开浏览器,访问 http://app.test,如果没有登录的话,先登录,然后在浏览器输入 http://app.test/logout 退出应用,应用退出后会跳转到服务端退出,再跳回 http://app.test

前面讲原理的时候说过,此时通过单点登录的其它客户端还是处于登录状态,目前使用的扩展包不能实现统一退出功能,如果需要的话可以自己实现,你可以将登录客户端状态通过 CAS 服务端进行管理,比如具体每个用户对应的 Session ID,然后通过监听器监听服务端退出事件,一旦退出,立即发送通知给所有已登录其它客户端,让它们销毁对应的 Session ID 即可完成统一退出:

这里,我就不展开了,感兴趣的同学可以自己去实现下,实现过程中有什么问题欢迎与我交流。


点赞 取消点赞 收藏 取消收藏

<< 上一篇: 基于 CAS 实现通用的单点登录解决方案(二):CAS 客户端搭建及单点登录测试

>> 下一篇: Laravel 底层是如何处理 HTTP 请求的