• cas的客户端应用是负载均衡,单点退出怎么办?


    之前的项目一直是单节点,这次在生产系统中使用了负载均衡,一个应用部署了两个节点,负载均衡策略未知。这样在使用时发现了这么一个问题:在单点退出后,应用有时候可以退出,但有时还在登陆状态,这就很郁闷了。

    我的cas版本是5.1.2。一点点排查,把每个节点的日志都打开,把日志级别设置成最小trace,功夫不负有心人,发现了问题:用户在浏览器访问的都是节点一,在cas单点退出后,cas服务端发送logoutRequest的post请求,结果负载均衡把请求发送到了节点二,节点二压根未访问过,logoutRequest请求当然不起作用。

    问题找到了,看官可能觉得楼主有点low,首先,负载均衡具体什么策略,为什么会发错节点;第二,负载均衡多节点为什么不用session共享。下面我一一解答。

    第一个,负载均衡不是我负责的,我只知道通过浏览器访问一个应用能保证每次都访问同个节点,这就保证了用户每次都携带登录信息。我以为采用的是nginx的ip的hash值,结果被告知不是,是通过硬件控制负载均衡的。

    第二个,之前公司项目差不多都是单节点,几乎所有的应用都未使用session共享,况且现在是上线的关键节点,现改也来不及,最好是修改cas客户端的东西,大家都更新一下jar包就可以了。

            logger.trace("Logout request:
    {}", logoutMessage);
            final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex");
            if (CommonUtils.isNotBlank(token)) {
                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.invalidate();
                    } catch (final IllegalStateException e) {
                        logger.debug("Error invalidating session.", e);
                    }
                    this.logoutStrategy.logout(request);
                }

    了解cas的同学知道,cas服务端发送logoutRequest的post请求,里面携带token信息,客户端就凭借这个token找到session。图中标红部分,当未找到时不起作用,解决之道就在此,我们拿到了token信息,然后向携带着token向应用的各个节点发送一次退出请求,这样就能找到session,然后失效退出了。

    就这样,问题找到了,就着手解决吧。

    第一步:修改cas客户端,当session为空时,发送post请求,上代码。

            logger.trace("Logout request:
    {}", logoutMessage);
            final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex");
            if (CommonUtils.isNotBlank(token)) {
                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.invalidate();
                    } catch (final IllegalStateException e) {
                        logger.debug("Error invalidating session.", e);
                    }
                    this.logoutStrategy.logout(request);
                }else{
                    if(CommonUtils.isNotBlank(this.balanceUrls)) {
                        String[] urls = balanceUrls.split(",");
                        for(String url : urls) {
                            Map<String, Object> map = new HashMap<String, Object>();
                            map.put("token",token);
                            try {
                    //发送post请求,不要在意我的工具类名字,复制来的 HttpClient.GET_JCMH_QX(map, url
    + "/logoutRequestOther"); }catch(Exception e) { logger.debug("Error HttpClient ", e); } } } }

    比之前的代码多了else语句,这里的balanceUrls(在filter的initParam维护),是负载均衡的转发地址,多个用,隔开,然后遍历发送post请求,请求路径统一为“/logoutRequestOther“。

    第二步在我们第一步的/logoutRequestOthe请求肯定是请求不到的,我们怎么处理呢?为了最小的改动,在cas客户端增加filter进行处理,增加LogoutForBalanceFilter类。

     一定要跟SingleSignOutFilter同目录,原因是我们需要用到 private SessionMappingStorage sessionMappingStorage ,session信息就存储在这个类中。

    package org.jasig.cas.client.session;
    
    import org.jasig.cas.client.util.CommonUtils;
    import org.jasig.cas.client.util.JsonUtil;
    import org.jasig.cas.client.util.XmlUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.InputStream;
    import java.util.Map;
    
    public class LogoutForBalanceFilter implements Filter {
    
        private final Logger logger = LoggerFactory.getLogger(LogoutForBalanceFilter.class);
    
        private SessionMappingStorage sessionMappingStorage;
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                FilterChain chain) throws IOException, ServletException {
    
            if (servletRequest instanceof HttpServletRequest) {
                HttpServletRequest req = (HttpServletRequest) servletRequest;
                String uri = req.getRequestURI();
                logger.debug("================== uri [{}]", uri);
                if (uri.contains("/logoutRequestOther")) {
                    if (sessionMappingStorage == null) {
                        sessionMappingStorage = getSessionMappingStorage();
                    }
             //获取post请求的参数 Map
    <?,?> map = JsonUtil.jsonStrToMap(getParams(req)); if(map != null) { String token = (String)map.get("token"); logger.debug("================== token [{}]", token); if (CommonUtils.isNotBlank(token)) {
                  //通过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.invalidate(); } catch (final IllegalStateException e) { logger.debug("Error invalidating session.", e); } //this.logoutStrategy.logout(request); } } } return; } chain.doFilter(servletRequest, servletResponse); }else { chain.doFilter(servletRequest, servletResponse); } } @Override public void destroy() { } protected static SessionMappingStorage getSessionMappingStorage() { return SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage(); } protected String getParams(HttpServletRequest request){ String body = ""; StringBuilder stringBuilder = new StringBuilder(); BufferedReader bufferedReader = null; InputStream inputStream = null; try { inputStream = request.getInputStream(); if (inputStream != null) { bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); char[] charBuffer = new char[128]; int bytesRead = -1; while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { stringBuilder.append(charBuffer, 0, bytesRead); } } else { stringBuilder.append(""); } } catch (IOException ex) { ex.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (bufferedReader != null) { try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } } body = stringBuilder.toString(); return body; } }

    至此客户端更改完毕。

    这样,同事们只需要更改下web.xml,增加一个filter,添加负载均衡转发的地址参数列表。

        <filter>
    
            <filter-name>Cas_Balance_Logout</filter-name>
    
            <filter-class>org.jasig.cas.client.session.LogoutForBalanceFilter</filter-class>
    
        </filter>
    
        <filter-mapping>
    
            <filter-name>Cas_Balance_Logout</filter-name>
    
            <url-pattern>/logoutRequestOther</url-pattern>
    
        </filter-mapping>
        
        <filter>

    <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>http://zsptsso.jlsw.tax.cn/cas</param-value> </init-param> <init-param> <param-name>balanceUrls</param-name> <param-value>http://123.14.62.125:8002/zntx,http://123.14.62.126:8002/zntx</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener>

    图中红色部分就是同事们需要增加的,是不是很简单呢,完美!

  • 相关阅读:
    学就要学好 就要学明白
    URL的基础
    各种waf识别
    Linux命令行上的上传和下载文件命令
    Linux服务器安全加固(三)
    Linux服务器安全加固(二)
    Linux服务器安全加固(一)
    Centos7配置SNMP服务
    Windows Server 系统通用安全基线配置详细
    Windows Server 2016 部署AD域控制器及添加AD域控制器
  • 原文地址:https://www.cnblogs.com/caozx/p/11985464.html
Copyright © 2020-2023  润新知