本文目标

基于SpringBoot + Maven 分别使用自动配置与手动配置过滤器方式实现CAS客户端登出及单点登出。

本文基于《CAS学习笔记三:SpringBoot自动/手动配置方式集成CAS单点登录》的代码扩充而来,完整代码见 https://github.com/hellxz/cas-integration-demo

CAS服务端配置

单点登出跟随 service 给出的跳转地址重定向功能 在 CAS 服务端默认是关闭的,所以需要先开启它。

vim webapps/cas/WEB-INF/classes/application.properties

在最下方追加配置项 cas.logout.followServiceRedirects=true,保存重启CAS服务端。

代码目录结构

以上红字仅对本文修改的部分进行说明,其余请参考之前单点登录的实现文章。

代码实现

仅增量介绍关键类

SpringBoot自动配置登出实现

CasClientConfigurerImpl.java

package com.hellxz.cas;import java.util.Map;import org.jasig.cas.client.boot.configuration.CasClientConfigurer;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.stereotype.Component;/*** cas-client-support-springboot 依赖提供了CAS客户端的自动配置,* 当自动配置不满足需要时,可通过实现{@link CasClientConfigurer}接口来重写需要自定义的逻辑*/
@Component
public class CasClientConfigurerImpl implements CasClientConfigurer {/*** 配置认证过滤器,添加忽略参数,使/logoutPage登出提示页免登录*/@Override@SuppressWarnings({ "rawtypes", "unchecked" })public void configureAuthenticationFilter(final FilterRegistrationBean authenticationFilter) {Map initParameters = authenticationFilter.getInitParameters();initParameters.put("ignorePattern", "/logoutPage");}}

上边这个配置类的作用是自定义认证过滤器,将 /logoutPage 排除不走认证逻辑,此页面用于显示登出提示。

CasAutoConfigApp.java

package com.hellxz.cas;import java.io.IOException;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;import org.jasig.cas.client.boot.configuration.EnableCasClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@SpringBootApplication
@RestController
@EnableCasClient
public class CasAutoConfigApp {@Value("${custom.cas.single-logout-url:}")public String casSingleLogoutUrl;public static void main(String[] args) {SpringApplication.run(CasAutoConfigApp.class, args);}@GetMapping("/test")public String test(HttpServletRequest request) {return "服务A测试通过";}/*** 首页,需要登录*/@GetMapping("/index")public String index(HttpServletRequest request) {//@formatter:offreturn "<h1>登录成功</h1><br><br>"+ "<a href=\"/logout\">退出登录</a><br><br>"+ "<a href=\"" + casSingleLogoutUrl + "\">全局退出登录</a>";//@formatter:on}/*** 登出提示页,免登录*/@GetMapping("/logoutPage")public String logoutPage(HttpServletResponse response) {//@formatter:offreturn "<h1>您已退出登录成功。</h1><br><br>"+ "<a href=\"/index\">去登录</a><br><br>"+ "<a href=\"" + casSingleLogoutUrl + "\">全局退出登录</a>";//@formatter:on}/*** 退出登录,跳转登出提示页*/@GetMapping("/logout")public void logout(HttpServletRequest request, HttpServletResponse response) throws IOException {HttpSession session = request.getSession(false);if (session != null) {// 过期会话session.invalidate();}// 跳转登出提示页response.sendRedirect("/logoutPage");}}

较上一迭代新增了 /index(主页)、/logoutPage(登出提示页)、/logout (客户端退出登录)这三个接口。其中只有 /logout 接口是免登录的,为了防止出现重定向回来自动登录的情况。

application.properties

这里启用了单点登出配置项,CasClientConfiguration 中的 casSingleSignOutFilter()casSingleSignOutListener() 这两个方法激活,注册Bean到MVC容器中。

自定义单点登出地址相当于拼接 CAS服务端登出地址与回调重定向地址,这里配置成免登录的客户端地址 /logoutPage

手动配置登出实现

CasConfig.java,是上一迭代的Config.java重命名而来。

package com.hellxz.cas;import java.util.EventListener;
import java.util.HashMap;
import java.util.Map;import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;@Configuration
public class CasConfig {/*** 自定义cas服务地址*/@Value("${custom.cas.casServerUrlPrefix:}")private String casServerUrlPrefix;/*** 自定义服务标识,格式为{protocol}:{hostName}:{port}*/@Value("${custom.cas.serverName:}")private String serverName;/*** 监听登出事件,清除session与token之间的映射关系及CAS会话记录*/@Beanpublic ServletListenerRegistrationBean<EventListener> casSingleSignOutListener() {ServletListenerRegistrationBean<EventListener> singleSignOutListener = new ServletListenerRegistrationBean<>();singleSignOutListener.setListener(new SingleSignOutHttpSessionListener());return singleSignOutListener;}@Bean@Order(0)public FilterRegistrationBean<SingleSignOutFilter> casSingleSignOutFilter() {FilterRegistrationBean<SingleSignOutFilter> registration = new FilterRegistrationBean<>();registration.setFilter(new SingleSignOutFilter());registration.setName("CAS Single Sign Out Filter");Map<String, String> initParams = new HashMap<>();initParams.put("casServerUrlPrefix", casServerUrlPrefix); // CAS服务端地址,会拼接为登录地址initParams.put("serverName", serverName); // 服务地址registration.setInitParameters(initParams);registration.addUrlPatterns("/*");return registration;}/*** 拦截所有请求,将未携带票据与会话中无票据的请求都重定向到CAS登录地址*/@Bean@Order(1)public FilterRegistrationBean<AuthenticationFilter> casAuthenticationFilter() {FilterRegistrationBean<AuthenticationFilter> registration = new FilterRegistrationBean<>();registration.setFilter(new AuthenticationFilter());registration.setName("CAS Authentication Filter");Map<String, String> initParams = new HashMap<>();initParams.put("casServerUrlPrefix", casServerUrlPrefix); // CAS服务端地址,会拼接为登录地址initParams.put("serverName", serverName); // 服务地址// 自定义忽略认证的路径或表达式,这里用来免登录访问【退出登录提示】页面initParams.put("ignorePattern", "/logoutPage");registration.setInitParameters(initParams);registration.addUrlPatterns("/*");return registration;}/*** 拦截所有请求,使用获取的票据向CAS服务端发起校验票据请求*/@Bean@Order(2)public FilterRegistrationBean<Cas30ProxyReceivingTicketValidationFilter> cas30TicketValidationFilter() {FilterRegistrationBean<Cas30ProxyReceivingTicketValidationFilter> registration = new FilterRegistrationBean<>();registration.setFilter(new Cas30ProxyReceivingTicketValidationFilter());registration.setName("CAS30 Ticket Validation Filter");Map<String, String> initParams = new HashMap<>();initParams.put("casServerUrlPrefix", casServerUrlPrefix); // CAS服务端地址,会拼接为服务校验地址initParams.put("serverName", serverName);registration.setInitParameters(initParams);registration.addUrlPatterns("/*");return registration;}/*** 包装HttpServletRequest,使CAS登录成功的用户名等信息存入请求中<br>* <br>* 登录成功后以下两个方法将不再返回null: <br>* * <pre>* HttpServletRequest#getUserPrincipal()* HttpServletRequest#getRemoteUser()* </pre>*/@Bean@Order(3)public FilterRegistrationBean<HttpServletRequestWrapperFilter> httpServletRequestWrapperFilter() {FilterRegistrationBean<HttpServletRequestWrapperFilter> registration = new FilterRegistrationBean<>();registration.setFilter(new HttpServletRequestWrapperFilter());registration.setName("HttpServletRequest Wrapper Filter");registration.addUrlPatterns("/*");return registration;}}

新增了 casSingleSignOutListener() (配置单点登出监听器)、casSingleSignOutFilter()(单点登出过滤器)以及 将登出提示页从认证过滤器处放行。

// 自定义忽略认证的路径或表达式,这里用来免登录访问【退出登录提示】页面
initParams.put("ignorePattern", "/logoutPage");

需注意单点登出过滤器的排序要早于认证过滤器、校验票据过滤器。

CasManualConfigApp.java

package com.hellxz.cas;import java.io.IOException;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@SpringBootApplication
public class CasManualConfigApp {/*** 自定义全局单点登出地址,由cas服务端地址/logout?service=当前serviceName/logoutPage组成<br>* 当cas全局登出(带TGC访问cas的/logout接口)成功后,会重定向service参数地址<br>* * <pre>* 需注意:service参数必须含登录时注册给CAS的serviceName,否则只废弃CAS会话而不会重定向* </pre>*/@Value("${custom.cas.casSingleLogoutUrl:}")private String casSingleLogoutUrl;public static void main(String[] args) {SpringApplication.run(CasManualConfigApp.class, args);}@GetMapping("/test")public String test(HttpServletRequest request) {return "服务B测试通过";}/*** 首页,需要登录*/@GetMapping("/index")public String index(HttpServletRequest request) {//@formatter:offreturn "<h1>登录成功</h1><br><br>"+ "<a href=\"/logout\">退出登录</a><br><br>"+ "<a href=\"" + casSingleLogoutUrl + "\">全局退出登录</a>";//@formatter:on}/*** 登出提示页,免登录*/@GetMapping("/logoutPage")public String logoutPage(HttpServletResponse response) {//@formatter:offreturn "<h1>您已退出登录成功。</h1><br><br>"+ "<a href=\"/index\">去登录</a><br><br>"+ "<a href=\"" + casSingleLogoutUrl + "\">全局退出登录</a>";//@formatter:on}/*** 退出登录,跳转登出提示页*/@GetMapping("/logout")public void logout(HttpServletRequest request, HttpServletResponse response) throws IOException {HttpSession session = request.getSession(false);if (session != null) {// 过期会话session.invalidate();}// 跳转登出提示页response.sendRedirect("/logoutPage");}
}

与 自动配置实现基本一致,添加几个接口供测试

application.properties

验证实现

以本地IP为 10.2.6.63,自动配置端口8081,手动配置端口 8082,CAS服务端192.168.56.104:8088/cas 为例

1、单点登录

启动自动配置服务,访问本地端口号8081,我这里是 http://10.2.6.63:8081/index ,访问首页立即跳转CAS登录页面

输入用户名与密码,casuser/Mellon,登录。如下图单点登录成功

2、客户端登出

先验证退出客户端登出,点 退出登录。如图进入登出成功提示页面

检察下 /logout 接口的 cookie值,此处是 95E21E8D67C363A7432C342EDACB4DE8

再点击 去登录,这是访问 /index,可以看到又登录成功了,而且这次没有手输账号密码,查看了cookie中的会话id为 6157E88046280B3E90A502016F98549A,与退出之前不是同一会话

3、单点登出

接下来验证CAS单点登出,点击 全局退出登录。如下图,可见访问CAS服务端 /logout接口,并传递了回调地址为登出提示页面,最终回到了提示页面。

我们再试验一下 去登录,验证是否需要手输账号密码登录。

如上图,的确需要手输账号才能登录,说明单点登出功能正常。

4、CAS客户端单点登出日志

由于自动配置项目我没配置单点登出 trace 等级日志,我们用 手动配置服务登录再全局退出下,看看日志。

启动手动配置服务,访问 http://10.2.6.63:8082/index,登录后再全局退出。日志如下:

2022-01-18 23:22:38.237 TRACE 24016 --- [nio-8082-exec-1] o.j.c.c.session.SingleSignOutHandler     : Ignoring URI for logout: /index
2022-01-18 23:22:40.401 TRACE 24016 --- [nio-8082-exec-4] o.j.c.c.session.SingleSignOutHandler     : Received a token request
2022-01-18 23:22:40.406 DEBUG 24016 --- [nio-8082-exec-4] o.j.c.c.session.SingleSignOutHandler     : Recording session for token ST-61-aJ6BwiVkekqtkqlIXmmyWAYq6sMlocalhost
2022-01-18 23:22:40.535 TRACE 24016 --- [nio-8082-exec-3] o.j.c.c.session.SingleSignOutHandler     : Ignoring URI for logout: /index
2022-01-18 23:22:43.698 TRACE 24016 --- [nio-8082-exec-5] o.j.c.c.session.SingleSignOutHandler     : Received a logout request
2022-01-18 23:22:43.699 TRACE 24016 --- [nio-8082-exec-5] o.j.c.c.session.SingleSignOutHandler     : Logout request:
<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="LR-37-W7V-UYdTLhGkwl7P2w2somtR" Version="2.0" IssueInstant="2022-01-18T10:22:43Z"><saml:NameID xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">@NOT_USED@</saml:NameID><samlp:SessionIndex>ST-61-aJ6BwiVkekqtkqlIXmmyWAYq6sMlocalhost</samlp:SessionIndex></samlp:LogoutRequest>
2022-01-18 23:22:43.702 DEBUG 24016 --- [nio-8082-exec-5] o.j.c.c.session.SingleSignOutHandler     : Invalidating session [DB43DC21DCC1663D64968E8DBD48B247] for token [ST-61-aJ6BwiVkekqtkqlIXmmyWAYq6sMlocalhost]
2022-01-18 23:22:43.712 TRACE 24016 --- [nio-8082-exec-2] o.j.c.c.session.SingleSignOutHandler     : Ignoring URI for logout: /logoutPage

可以看到:

  • 登录成功的日志 Recording session for token ST-61-aJ6BwiVkekqtkqlIXmmyWAYq6sMlocalhost
  • 单点登出的日志: Received a logout requestLogout request: <samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="LR-37-W7V-UYdTLhGkwl7P2w2somtR" Version="2.0" IssueInstant="2022-01-18T10:22:43Z"><saml:NameID xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">@NOT_USED@</saml:NameID><samlp:SessionIndex>ST-61-aJ6BwiVkekqtkqlIXmmyWAYq6sMlocalhost</samlp:SessionIndex></samlp:LogoutRequest>
  • 过期当前客户端会话的 Invalidating session [DB43DC21DCC1663D64968E8DBD48B247] for token [ST-61-aJ6BwiVkekqtkqlIXmmyWAYq6sMlocalhost]

至此验证客户端登出及单点登出功能一切正常。

自动配置和手动配置这两个工程的效果是一样的,笔者已经亲身测试OK,就不在此重复表述了。

总结

本次编写的 demo 恰如其分地验证了CAS客户端登出与单点登出的流程,即客户端登出(过期自己)及单点登出(过期自己以及所有相关客户端)。

参考:

  • https://github.com/cas-projects/cas-sample-java-webapp/blob/master/src/main/webapp/WEB-INF/web.xml
  • https://github.com/apereo/java-cas-client

本文同步于本人博客园(hellxz.cnblogs.com) 与 CSDN(https://blog.csdn.net/u012586326),禁止转载。

CAS学习笔记五:SpringBoot自动/手动配置方式集成CAS单点登出相关推荐

  1. Spring框架学习笔记09:基于XML配置方式搭建SSM框架实现用户登录

    文章目录 一.采用MVC架构 二.用户登录运行效果 三.基于XML配置方式搭建SSM框架实现用户登录 (一)创建数据库与表 - simonshop (t_user) 1.创建数据库 - simonsh ...

  2. springAop学习笔记(二,springboot进本配置和使用)

    配置 1.引入aop jar包 2.在主类上面加@EnableAspectJAutoProxy 这里要说一句:市面上常用有两种aop实现方式第一种是springaop,第二种是AspectJ,但是原始 ...

  3. Spring框架学习笔记07:基于XML配置方式使用Spring MVC

    文章目录 一.Spring MVC概述 1.MVC架构 2.Spring MVC 3.使用Spring MVC的两种方式 二.基于XML配置与注解的方式使用Spring MVC (一)创建Spring ...

  4. Spring框架学习笔记08:基于Java配置方式使用Spring MVC

    文章目录 一.基于Java配置与注解的方式使用Spring MVC 1.创建Maven项目 - SpringMVCDemo2020 2.在pom.xml文件里添加相关依赖 3.创建日志属性文件 - l ...

  5. Spring框架学习笔记11:基于Java配置方式SSM框架西蒙购物网

    文章目录 一.网站功能需求 二.网站设计思路 1.设计模式 2.网站前台 3.网站后台 4.购物流程图 三.网站运行效果 四.网站实现步骤 (一)创建数据库与表 1.创建数据库 - simonshop ...

  6. Spring框架学习笔记10:基于XML配置方式SSM框架西蒙购物网

    文章目录 一.网站功能需求 二.网站设计思路 1.设计模式 2.网站前台 3.网站后台 4.购物流程图 三.网站运行效果 四.网站实现步骤 (一)创建数据库与表 1.创建数据库 - simonshop ...

  7. SpringBoot学习笔记(4)----SpringBoot中freemarker、thymeleaf的使用

    1. freemarker引擎的使用 如果你使用的是idea或者eclipse中安装了sts插件,那么在新建项目时就可以直接指定试图模板 如图: 勾选freeMarker,此时springboot项目 ...

  8. 【AngularJs学习笔记五】AngularJS从构建项目开始

    为什么80%的码农都做不了架构师?>>>    #0 系列目录# AngularJs学习笔记 [AngularJs学习笔记一]Bower解决js的依赖管理 [AngularJs学习笔 ...

  9. SpringBoot学习笔记(9)----SpringBoot中使用关系型数据库以及事务处理

    在实际的运用开发中,跟数据库之间的交互是必不可少的,SpringBoot也提供了两种跟数据库交互的方式. 1. 使用JdbcTemplate 在SpringBoot中提供了JdbcTemplate模板 ...

最新文章

  1. SQL Server 2008中的Pivot和UnPivot
  2. 计算机缺少log怎么解决方法,win10 计算机丢失logmanager怎么办_win10丢失log.dll文件的解决方法-win7之家...
  3. linux环境下安装软件 快速,不超时
  4. C# 日志管理框架:Common.Logging和log4net
  5. rto初始化和计算_TCP系列13—重传—3、协议中RTO计算和RTO定时器维护
  6. InfluxDB学习之InfluxDB数据保留策略(Retention Policies)
  7. 使用Spark Streaming SQL基于时间窗口进行数据统计
  8. unity 插件 color picker htc手柄控制,扣动扳机生成三维点线
  9. 游戏出海成为全球化趋势
  10. matlab截取图像像素,图像中获取图像的像素值
  11. Android 12 已来,你的 App 崩溃了吗?
  12. 守卫解救acm_让作家阻止了它的解救
  13. 关于JAVA外包的解释及优劣势
  14. 使用Python绘制圣诞树教程(附源代码)
  15. 单点登录(4):单点登录实现(附源码)
  16. android 下载保存视频到本地相册刷新 机型适配问题
  17. 舆情网比较好用及比较实用的舆情系统工具参考
  18. 共同学习Java源代码-多线程与并发-FutureTask类(三)
  19. Pynq_Z2-hdmi输出图像、文字流程(VDMA)
  20. 较详细的MongDB工具类

热门文章

  1. react 使用注意:
  2. 团队开发-极速蜗牛-NABC模型
  3. java微信公众号分享功能
  4. HTML5 + CSS3 的常用 button 样式
  5. C#植物大战僵尸中文版
  6. linux命令ls -rlt,如何使用BPF将SSH会话转换为结构化事件
  7. 2021年世界互联网领先科技成果提名项目之类脑芯片 KA200
  8. 关系抽取论文整理——早期文献
  9. 基于java会议管理系统设计(含源文件)
  10. pc usb充电测试软件,快速充电不是谎言?实测PC USB充电功能