CAS-5.2.6单点登录-退出原理

退出原理流程图:

cas的退出有三种模式:

 

  • NONE:不支持单点登录
  • BACK_CHANNEL:隐式退出(默认)
  • FRONT_CHANNEL:显式退出 

参数说明

参考官网地址 

 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

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值