• cas-client无法全部退出问题


    client是用域名访问的

    1.如果客户端是一台服务器,查看cas-server的debug日志,发现server在客户端登录成功后,会记录下客户端的域名和票据,当用户从客户端退出时,会调server的logout,server取出所有同一票据的客户端域名,循环清除session,但如果server服务器没有配置客户端域名对应的ip host文件,server找不到该域名对应的主机,因此清不掉session,导致无法退出。

    解决方案:在server服务器配置host文件,配置客户端域名和ip映射。

    2.如果客户端是集群模式

    1. 单点注销为保证注销成功,需保证配置的域名、IP地址在单点登录服务端可以请求到,这样单点登录在做回调的时候才可以通过存储st信息把登录的客户端注销成功
    2. 集群环境下单点登录注销会存在注销不成功情况,原因是单点登录服务在做回调的时候回可能分发在非登录的tomcat服务上

    解决方案:

    1:Nginx+tomcat+redis session共享,该实现方案,不管注销落在哪个tomcat都可以把redis中session注销成功。(未实践)

    2:修改cas-client源码,单点登录客户端集群采用广播的方式,注销时向所有节点发起注销session操作。(实践成功)

    基于cas-client-core-3.4.1实现

    参考 http://blog.csdn.net/zhurhyme/article/details/74726008?locationNum=5&fps=1

    基于cas-client-core-3.2.1实现

    参考 https://www.jianshu.com/p/4c6f010c420e

    web.xml修改以下配置

    <!-- 单点登录退出监听 -->
        <listener>  
            <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>  
          </listener>  
        <!-- 单点注销过滤器。 -->
          <filter>  
                <filter-name>CAS Single Sign Out Filter</filter-name>  
                <filter-class>com.cas.SingleSignOutFilter</filter-class>
                <init-param>
                    <description>cas client cluster nodes</description>
                       <param-name>clusterNodeUrls</param-name>
                       <param-value>http://ip:8080/client1/,http://ip:8081/client2/</param-value>
                </init-param>
          </filter>  
          <filter-mapping>  
                <filter-name>CAS Single Sign Out Filter</filter-name>  
                <url-pattern>/*</url-pattern>  
          </filter-mapping>  

     修改cas-client 的SingleSignOutFilter和SingleSignOutHandler

    import java.io.IOException;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import org.jasig.cas.client.session.SessionMappingStorage;
    import org.jasig.cas.client.util.AbstractConfigurationFilter;
    
    public class SingleSignOutFilter extends AbstractConfigurationFilter {
        private static final SingleSignOutHandler handler = new SingleSignOutHandler();
    
        public void init(FilterConfig filterConfig) throws ServletException {
            if (!isIgnoreInitConfiguration()) {
                handler.setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket"));
                handler.setLogoutParameterName(getPropertyFromInitParams(filterConfig, "logoutParameterName", "logoutRequest"));
                //clusterNodeUrls
                handler.setClusterNodeUrls(getPropertyFromInitParams(filterConfig, "clusterNodeUrls", ""));
            }
            handler.init();
        }
    
        public void setArtifactParameterName(String name) {
            handler.setArtifactParameterName(name);
        }
    
        public void setLogoutParameterName(String name) {
            handler.setLogoutParameterName(name);
        }
    
        public void setSessionMappingStorage(SessionMappingStorage storage) {
            handler.setSessionMappingStorage(storage);
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest,
                ServletResponse servletResponse, FilterChain filterChain)
                throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            if (handler.isTokenRequest(request)) {
                handler.recordSession(request);
            } else if(handler.isLogoutRequest(request)){//cas-server logout请求
                handler.destroySession(request);
                return;
            } else if(handler.isLogoutRequestFromClusterNode(request)){//接收其它节点发送的http logout请求 
                //清除本节点session
                handler.destroySessionFromClusterNode(request);
                return;
            }
    
            filterChain.doFilter(servletRequest, servletResponse);
        }
        
        @Override
        public void destroy() {
        }
        
        protected static SingleSignOutHandler getSingleSignOutHandler() {
            return handler;
        }
    }
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.apache.http.NameValuePair;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.entity.UrlEncodedFormEntity;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.client.utils.HttpClientUtils;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.message.BasicNameValuePair;
    import org.jasig.cas.client.session.HashMapBackedSessionMappingStorage;
    import org.jasig.cas.client.session.SessionMappingStorage;
    import org.jasig.cas.client.util.CommonUtils;
    import org.jasig.cas.client.util.XmlUtils;
    
    public final class SingleSignOutHandler {
        private final Log log = LogFactory.getLog(getClass());
    
        private SessionMappingStorage sessionMappingStorage = new HashMapBackedSessionMappingStorage();
    
        private String artifactParameterName = "ticket";
    
        private String logoutParameterName = "logoutRequest";
        
        private String logoutParameterClusterName = "logoutRequestCluster";
        ///clusterNodeUrls
        private String clusterNodeUrls;
    
        public void setSessionMappingStorage(SessionMappingStorage storage) {
            this.sessionMappingStorage = storage;
        }
    
        public SessionMappingStorage getSessionMappingStorage() {
            return this.sessionMappingStorage;
        }
    
        public void setArtifactParameterName(String name) {
            this.artifactParameterName = name;
        }
    
        public void setLogoutParameterName(String name) {
            this.logoutParameterName = name;
        }
    
        public void setClusterNodeUrls(String clusterNodeUrls) {
            this.clusterNodeUrls = clusterNodeUrls;
        }
    
        public void init() {
            CommonUtils.assertNotNull(this.artifactParameterName,"artifactParameterName cannot be null.");
            CommonUtils.assertNotNull(this.logoutParameterName,"logoutParameterName cannot be null.");
            CommonUtils.assertNotNull(this.sessionMappingStorage,"sessionMappingStorage cannote be null.");
        }
    
        public boolean isTokenRequest(HttpServletRequest request) {
            return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request,this.artifactParameterName));
        }
    
        public boolean isLogoutRequest(HttpServletRequest request) {
            log.info("isLogoutRequest begin----");
            log.info(request.getRequestURL());
            log.info("request.getMethod()=" + request.getMethod());
            log.info("CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName,this.safeParameters))="
                            + CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName)));
            log.info("isLogoutRequest end----");
            return ("POST".equals(request.getMethod())) && (!isMultipartRequest(request))
                    && (CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName)));
        }
        
        /**
         * 判断是否是其它节点发送的logout通知
         * @param request
         * @return
         */
        public boolean isLogoutRequestFromClusterNode(HttpServletRequest request) {
            log.info("isLogoutRequestFromClusterNode begin---");
            log.info("clusterNodeUrls=" + this.clusterNodeUrls);
            log.info("request.getParameter(this.logoutParameterClusterName)=" + request.getParameter(this.logoutParameterClusterName));
            log.info("isLogoutRequestFromClusterNode end---");
            return (!isMultipartRequest(request)) && ("true".equals(request.getParameter(this.logoutParameterClusterName)));
        }
    
        public void recordSession(HttpServletRequest request) {
            HttpSession session = request.getSession(true);
    
            String token = CommonUtils.safeGetParameter(request,this.artifactParameterName);
            log.info("--------recordSession-------------token:"+token);
            if (this.log.isDebugEnabled()) {
                this.log.debug("Recording session for token " + token);
            }
            try {
                this.sessionMappingStorage.removeBySessionById(session.getId());
            } catch (Exception e) {
            }
    
            this.sessionMappingStorage.addSessionById(token, session);
        }
    
        public void destroySession(HttpServletRequest request) {
            log.info("destroySession begin---");
            String logoutMessage = CommonUtils.safeGetParameter(request,this.logoutParameterName);
            if (this.log.isTraceEnabled()) {
                this.log.trace("Logout request:
    " + logoutMessage);
            }
            String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex");
            if (CommonUtils.isNotBlank(token)) {
                HttpSession session = this.sessionMappingStorage.removeSessionByMappingId(token);
    
                if (session != null) {//session在当前节点
                    log.info("destroySession session在当前节点------");
                    String sessionID = session.getId();
    
                    if (this.log.isDebugEnabled()) {
                        this.log.debug("Invalidating session [" + sessionID + "] for token [" + token + "]");
                    }
                    try {
                        session.invalidate();
                    } catch (IllegalStateException e) {
                        this.log.debug("Error invalidating session.", e);
                    }
                }else {//session不在当前节点
                    log.info("destroySession session不在当前节点------");
                    //清除其他节点,采用广播形式发送http请求
                    destroySessionOfClusterNodes(token);
                }
            }
            log.info("destroySession end---");
        }
    
        /**
         * 采用广播形式发送http请求,通知其他节点清除session
         * @author xubo 2018-3-21
         * @param token
         */
        private void destroySessionOfClusterNodes(String token) {
            //广播到所有节点
            log.info("destroySessionOfClusterNodes--begin-----:" + token);
            if(this.clusterNodeUrls != null && this.clusterNodeUrls.length() > 0){
                log.info(clusterNodeUrls);
                String[] clusters = this.clusterNodeUrls.split(",");
                for (String url : clusters) {
                    HttpClient httpClient = new DefaultHttpClient();
                    
                    HttpPost httpPostReq = new HttpPost(url);
                    List<NameValuePair> paramList = new ArrayList<NameValuePair>();
                    paramList.add(new BasicNameValuePair(this.logoutParameterClusterName,"true"));
                    paramList.add(new BasicNameValuePair(this.artifactParameterName,token));
                    try {
                        httpPostReq.setEntity(new UrlEncodedFormEntity(paramList));
                        httpClient.execute(httpPostReq);
                    } catch (Exception e) {
                        log.debug("Error destroySessionOfClusterNodes.",e);
                    }finally{
                        HttpClientUtils.closeQuietly(httpClient);
                    }
                }
            }
            log.info("destroySessionOfClusterNodes--end-----:" + token);
        }
    
        /**
         * 接收从其它节点的通知,清除session
         * @author xubo 2018-3-21
         * @param request
         */
        public void destroySessionFromClusterNode(HttpServletRequest request){
            String token = request.getParameter(this.artifactParameterName);
            log.info("destroySessionFromClusterNode----begin---:" + token);
            if(CommonUtils.isNotBlank(token)){
                final HttpSession session = sessionMappingStorage.removeSessionByMappingId(token);
                
                if(session != null){
                    String sessionID = session.getId();
                    
                    if(log.isDebugEnabled()){
                        log.debug("Invalidating session[" + sessionID +"] for token [" + token + "]");
                    }
                    try {
                        session.invalidate();
                    } catch (final IllegalStateException e) {
                        log.debug("Error invalidating session",e);
                    }
                }
            }
            log.info("destroySessionFromClusterNode----end---:" + token);
        }
        
        private boolean isMultipartRequest(HttpServletRequest request) {
            return (request.getContentType() != null) && (request.getContentType().toLowerCase().startsWith("multipart"));
        }
    }
  • 相关阅读:
    python join的用法
    python json中的 dumps loads函数
    ubuntu 初始配置
    如何为ubuntu配置java环境
    Ubuntu系统如何安装软件
    取模与取余
    基本数据类型
    js面试题——作用域和闭包
    js面试题-原型和原型链
    js面试题-变量类型和计算
  • 原文地址:https://www.cnblogs.com/jason123/p/8618624.html
Copyright © 2020-2023  润新知