退出原理流程图:
cas的退出有三种模式:
参考官网地址
https://apereo.github.io/cas/5.2.x/installation/Configuration-Properties.html#logout
#配置单点登出
#配置允许登出后跳转到指定页面
cas.logout.followServiceRedirects=false
#跳转到指定页面需要的参数名为 service
cas.logout.redirectParameter=service
#登出后需要跳转到的地址,如果配置该参数,service将无效。
cas.logout.redirectUrl=https://www.taobao.com
#在退出时是否需要 确认退出提示 true弹出确认提示框 false直接退出
cas.logout.confirmLogout=true
#是否移除子系统的票据
cas.logout.removeDescendantTickets=true
#禁用单点登出,默认是false不禁止
#cas.slo.disabled=true
#默认异步通知客户端,清除session
#cas.slo.asynchronous=true
cas 默认登出后默认会跳转到CASServer的登出页,若想跳转到其它资源,可在/logout的URL后面加上service=jumpurl,例如:https://www.server.com:8443/cas/logout?service=https://www.baidu.com.com 但默认servcie跳转不会生效,需要在 cas服务端的application.properties添加cas.logout.followServiceRedirects=true .配置了cas.slo.disabled=true 将禁用单点登出。调用登出将无效.
客户端退出模式必须配置:
/**
* 登出过滤器
* @return
*/
@Bean
public FilterRegistrationBean filterSingleRegistration() {
SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
singleSignOutFilter.setCasServerUrlPrefix(casConfig.getServerUrlPrefix());
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setName("singleSignOutFilter");
filterRegistrationBean.setFilter(new DelegatingFilterProxy(singleSignOutFilter));
filterRegistrationBean.setOrder(2);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
return filterRegistrationBean;
}
SingleSignOutFilter源码:
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
//判断 初始化
if (!this.handlerInitialized.getAndSet(true)) {
HANDLER.init();
}
//判断是正常请求还是退出请求
if (HANDLER.process(request, response)) {
filterChain.doFilter(servletRequest, servletResponse);
}
}
public boolean process(final HttpServletRequest request, final HttpServletResponse response) {
//正常请求
if (isTokenRequest(request)) {
logger.trace("Received a token request");
recordSession(request);
return true;
}
//退出请求
if (isLogoutRequest(request)) {
logger.trace("Received a logout request");
//销毁session
destroySession(request);
return false;
}
logger.trace("Ignoring URI for logout: {}", request.getRequestURI());
return true;
}
private void recordSession(final HttpServletRequest request) {
final HttpSession session = request.getSession(this.eagerlyCreateSessions);
if (session == null) {
logger.debug("No session currently exists (and none created). Cannot record session information for single sign out.");
return;
}
final String token = CommonUtils.safeGetParameter(request, this.artifactParameterName, this.safeParameters);
logger.debug("Recording session for token {}", token);
try {
this.sessionMappingStorage.removeBySessionById(session.getId());
} catch (final Exception e) {
// ignore if the session is already marked as invalid. Nothing we can do!
}
//保存session
sessionMappingStorage.addSessionById(token, session);
}
private boolean isLogoutRequest(final HttpServletRequest request) {
if ("POST".equalsIgnoreCase(request.getMethod())) {
return !isMultipartRequest(request)
&& CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName,
this.safeParameters));
}
if ("GET".equalsIgnoreCase(request.getMethod())) {
return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters));
}
return false;
}
private void destroySession(final HttpServletRequest request) {
//在服务端设置完后跳转到客户端互获取
String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters);
//获取退出信息
if (CommonUtils.isBlank(logoutMessage)) {
logger.error("Could not locate logout message of the request from {}", this.logoutParameterName);
return;
}
if (!logoutMessage.contains("SessionIndex")) {
logoutMessage = uncompressLogoutMessage(logoutMessage);
}
logger.trace("Logout request:\n{}", logoutMessage);
final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex");
if (CommonUtils.isNotBlank(token)) {
//删除session中的信息
final HttpSession session = this.sessionMappingStorage.removeSessionByMappingId(token);
if (session != null) {
final String sessionID = session.getId();
logger.debug("Invalidating session [{}] for token [{}]", sessionID, token);
try {
//销毁session
session.invalidate();
} catch (final IllegalStateException e) {
logger.debug("Error invalidating session.", e);
}
this.logoutStrategy.logout(request);
}
}
}
服务端退出源码:
DefaultSingleLogoutServiceMessageHandler类,他是负责发送退出请求到我们客户端的,实现了SingleLogoutServiceMessageHandler 这个接口,找到这接口的时候,发现里面就一个方法
LogoutRequest handle(WebApplicationService singleLogoutService, String ticketId);
@Override
public LogoutRequest handle(final WebApplicationService singleLogoutService, final String ticketId) {
//判断是否已经登出
if (singleLogoutService.isLoggedOutAlready()) {
LOGGER.debug("Service [{}] is already logged out.", singleLogoutService);
return null;
}
//处理服务注销请求
final WebApplicationService selectedService = WebApplicationService.class.cast(
this.authenticationRequestServiceSelectionStrategies.resolveService(singleLogoutService));
LOGGER.debug("Processing logout request for service [{}]...", selectedService);
//取出这个注册的service服务的信息
final RegisteredService registeredService = this.servicesManager.findServiceBy(selectedService);
//判断是否支持退出
if (!serviceSupportsSingleLogout(registeredService)) {
LOGGER.debug("Service [{}] does not support single logout.", selectedService);
return null;
}
LOGGER.debug("Service [{}] supports single logout and is found in the registry as [{}]. Proceeding...", selectedService, registeredService);
//获取logout的url,这个是我们自己注册进去的
final URL logoutUrl = this.singleLogoutServiceLogoutUrlBuilder.determineLogoutUrl(registeredService, selectedService);
LOGGER.debug("Prepared logout url [{}] for service [{}]", logoutUrl, selectedService);
if (logoutUrl == null) {
LOGGER.debug("Service [{}] does not support logout operations given no logout url could be determined.", selectedService);
return null;
}
LOGGER.debug("Creating logout request for [{}] and ticket id [{}]", selectedService, ticketId);
//封装退出的消息内容,将退出请求以及st封装起来
final DefaultLogoutRequest logoutRequest = new DefaultLogoutRequest(ticketId, selectedService, logoutUrl);
LOGGER.debug("Logout request [{}] created for [{}] and ticket id [{}]", logoutRequest, selectedService, ticketId);
//判断是哪种模式下的退出请求,cas服务器分为三种
final RegisteredService.LogoutType type = registeredService.getLogoutType() == null
? RegisteredService.LogoutType.BACK_CHANNEL : registeredService.getLogoutType();
LOGGER.debug("Logout type registered for [{}] is [{}]", selectedService, type);
switch (type) {
case BACK_CHANNEL:
//发送通知
if (performBackChannelLogout(logoutRequest)) {
logoutRequest.setStatus(LogoutRequestStatus.SUCCESS);
} else {
logoutRequest.setStatus(LogoutRequestStatus.FAILURE);
LOGGER.warn("Logout message is not sent to [{}]; Continuing processing...", singleLogoutService.getId());
}
break;
default:
LOGGER.debug("Logout operation is not yet attempted for [{}] given logout type is set to [{}]", selectedService, type);
logoutRequest.setStatus(LogoutRequestStatus.NOT_ATTEMPTED);
break;
}
return logoutRequest;
}
public boolean performBackChannelLogout(final LogoutRequest request) {
try {
LOGGER.debug("Creating back-channel logout request based on [{}]", request);
final String logoutRequest = this.logoutMessageBuilder.create(request);
final WebApplicationService logoutService = request.getService();
//将发送退出后的设置为已发送
logoutService.setLoggedOutAlready(true);
LOGGER.debug("Preparing logout request for [{}] to [{}]", logoutService.getId(), request.getLogoutUrl());
//封装消息
final LogoutHttpMessage msg = new LogoutHttpMessage(request.getLogoutUrl(), logoutRequest, this.asynchronous);
LOGGER.debug("Prepared logout message to send is [{}]. Sending...", msg);
发送消息
return this.httpClient.sendMessageToEndPoint(msg);
} catch (final Exception e) {
LOGGER.error(e.getMessage(), e);
}
return false;
}
https://blog.csdn.net/qq_34021712/article/details/81515317