单点登录满足内外网映射问题、内外网映射端口丢失问题

1、cas-client-core 代码修改

       一般项目绝大数都是部署于某一个网络,要吗在内网,要不在外网,部署在内网的目前很多都通过VPN进行内网的访问。但对于一些项目是部署在内网,然后通过网络路由映射方式进行外网的访问,一般情况如果是通过自己开发的登陆此问题不需要进行任何改动,而当您使用了通用的CAS统一认证服务时,由于WEB应用工程中 web.xml配置的CAS地址是固定的,而不是一个动态的地址,当将WEB应用服务器例如TOMCAT端口映射外网后,在访问应用时会自动根据在web.xml文件中去配置对应的CAS地址,而此时的地址只能是内网使用,外网自然无法找到,则无法登陆,而由于项目的本身需要,必须要同时内外网都能访问,而应用已使用了CAS再来变更带来一些不便,同时CAS统一认证方面安全性还是较为有优势的,根据这一情况,前期进行了大量的咨询,都没能找得很好的解决办法,对于公司内部也无人能够解决,后通过百度搜索到了大量信息,并进行了测试,发现很多都无法满足自己的需要,后在无意中发现CNBlogs的一位作者名为BetterFuture的在自己博文中对CAS内外网功能的一个介绍,但由于没有进行全部的解说和一些代码的问题,并未能成功解决,后联系上了作者,并在其指导下完成自己的项目内网外功能,在此本人根据自己项目的情况进行了一次总结,以便于帮助更多的人;本案例以CAS-Client-3.2.1为例进行说明,对应的服务端为对应CAS-Server-4.0上,其不同版本源码虽有所改变,但此方法仍适用。

1)在org.jasig.cas.client.util下继续新增工具类PropertiesUtil代码如下:

package org.jasig.cas.client.util;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class PropertiesUtil {
	public static Map readProperties(String path) {

	        Map map = new HashMap<>();
	        try {
	            Properties props = new Properties();
	            props.load(new FileInputStream(path));
	            Enumeration enum1 = props.propertyNames();
	            while(enum1.hasMoreElements()) {
	              String strKey = (String) enum1.nextElement();
	              String strValue = props.getProperty(strKey);
	              map.put(strKey, strValue);
	            }
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	        return map;
	    }
}

2)在org.jasig.cas.client.util下继续新增工具类HttpConnectionUtil 代码如下:

package org.jasig.cas.client.util;

import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;

public class HttpConnectionCustom{
	/**
	 *  测试网络是否能连接通畅
	 */
	public static boolean isConnection(String serviceURL) {
		try {
			URL url = new URL(serviceURL);
			HttpURLConnection connection =(HttpURLConnection) url.openConnection();
			connection.setConnectTimeout(2000);//2s超时
			connection.setReadTimeout(2000);
			int state = connection.getResponseCode();
			if(state == 200) {
				return true;
			}else {
				return false;
			}
		}catch (Exception e) {
			return false;
		}
	}


	/**
	 * 测试ip是否有效
	 * @param ipSection
	 * @param ip
	 * @return
	 */
	public static boolean ipIsValid(String ipSection, String ip){
		if(ipSection == null){
			throw new NullPointerException("IP段不能为空!");
			}
		if(ip ==null) {
			throw new NullPointerException("IP不能为空!");
			}
		ipSection = ipSection.trim();
		ip = ip.trim();
		final String REGX_IP = "((25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)";
		final String REGX_IPB = REGX_IP + "\\-" + REGX_IP;
		if(!ipSection.matches(REGX_IPB) || !ip.matches(REGX_IP)){return false;}
		int idx = ipSection.indexOf('-');
		String[] sips = ipSection.substring(0, idx).split("\\.");
		String[] sipe = ipSection.substring(idx+1).split("\\.");
		String[] sipt = ip.split("\\.");
		long ips = 0L, ipe = 0L, ipt = 0L;
		for (int i = 0; i < 4; ++i) {
			ips = ips << 8 | Integer.parseInt(sips[i]);
			ipe = ipe << 8 | Integer.parseInt(sipe[i]);
			ipt = ipt << 8 | Integer.parseInt(sipt[i]);
		}
		if(ips > ipe){
			long t = ips;ips = ipe;ipe = t;
			}
		return ips <= ipt && ipt <= ipe;
		}

	/**
	* 获取配置文件信息
	*
	* @return
	*/
	public static Map getServiceUrlMap() {
		return PropertiesUtil.readProperties(HttpConnectionCustom.class.getResource("/").getPath() + "cas-service.properties");
	}
}

3)在org.jasig.cas.client.util下继续新增工具类CustomConfigUtil,代码为:

package org.jasig.cas.client.util;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

public class CustomConfigUtil {
	/**
     * @Description 获取属性文件中的属性信息
     * @return
     */
    public static Map getCustomConfig( final ServletContext servletContext,final HttpServletRequest request) {
        Map map = new HashMap<>();
        Map map2 = HttpConnectionCustom.getServiceUrlMap();
        //读取配置文件
        Map segmentMap = PropertiesUtil.readProperties(CustomConfigUtil.class.getResource("/").getPath() + "segment.properties");
        boolean falg = false;
        //获取外网请求nginx客户端的IP
        String  IP = request.getHeader("X-Forwarded-For");
        if(IP==null) {
        	 //获取nginx负载均衡后的IP
        	IP = request.getRemoteAddr();
        };
        for (String key : segmentMap.keySet()) {
            //判断是否属于某个网段
            falg =  HttpConnectionCustom.ipIsValid(segmentMap.get(key),IP);
            if(falg){
                break;
            }
        }
        // 判断请求是从外网访问还是从内网访问
        if (falg) {
            //client客户端地址
            map.put("client", map2.get("cas.inClient"));
            //cas服务器地址
            map.put("casServerTicketUrl", map2.get("cas.inCasServer"));
            //cas服务器地址
            map.put("casServerTicket", map2.get("cas.inCasServerTicket"));
        } else {
            //client客户端地址
        	  map.put("client", map2.get("cas.outClient"));
              //cas服务器地址
              map.put("casServerTicketUrl", map2.get("cas.outCasServer"));
             //cas服务器地址
              map.put("casServerTicket", map2.get("cas.outCasServerTicket"));
          }
          return map;
      }
}

4)、完成上述源代码的增加工具类后,开始修改CAS中的源代码了,首先修改AuthenticationFilte ,添加静态属性:

public static final String CONST_CAS_GATEWAY = "_const_cas_gateway_";

然后找到方法:doFilter 方法内容修改为:

	public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest)servletRequest;
		HttpServletResponse response = (HttpServletResponse)servletResponse;
		HttpSession session = request.getSession(false);
		Assertion assertion = session != null ? (Assertion)session.getAttribute("_const_cas_assertion_") : null;
		if (assertion != null) {
			filterChain.doFilter(request, response);
			return;
     }
		//ignoreUrl跳出filter
		String requestUrl = request.getRequestURI().toString();
		this.log.info("requestUrl==========="+requestUrl);
		PathMatcher matcher = new AntPathMatcher();
		if(ignoreUrlArray != null){
			for(String ignoreUrl : ignoreUrlArray){
				boolean flag = matcher.match(requestUrl,ignoreUrl);
				if(!flag){
					flag = requestUrl.indexOf(ignoreUrl)>0;
				}
				if(flag){
					this.log.info("ignoreUrl========= " + ignoreUrl + "====== pass sso authentication");
					filterChain.doFilter(request, response);
					return;
				}
			}
		}
		String serviceUrl = constructServiceUrl(request, response,"auth");
		String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());
		boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);

		if ((CommonUtils.isNotBlank(ticket)) || (wasGatewayed)) {
			filterChain.doFilter(request, response);
			return;
     }
		this.log.debug("no ticket and no assertion found");
		String modifiedServiceUrl;
		if (this.gateway) {
			this.log.debug("setting gateway attribute in session");
			modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
		} else {
			modifiedServiceUrl = serviceUrl;
     }
		if (this.log.isDebugEnabled()) {
			this.log.debug("Constructed service url: " + modifiedServiceUrl);
     }
		Map config = CustomConfigUtil.getCustomConfig(request.getServletContext(),request);
		String casServerLoginUrls = config.get("casServerTicketUrl");
		String urlToRedirectTo = CommonUtils.constructRedirectUrl(casServerLoginUrls, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
		if (this.log.isDebugEnabled()) {
			this.log.debug("redirecting to \"" + urlToRedirectTo + "\"");
     }

		response.sendRedirect(urlToRedirectTo);
   }

5)、由于修改了constructServiceUrl这个方法,增加了一个参数,由原来该方法只有两个参数 现在修改为3个,需要找到该方法的类AbstractCasFilter(源代码的第 113或114行中的constructServiceUrl方法)进行修改重载该方法:

protected final String constructServiceUrl(final HttpServletRequest request, final HttpServletResponse response,final String type) {
    	Map config = CustomConfigUtil.getCustomConfig(request.getServletContext(),request);
        if("auth".equals(type)){
            this.serverName = config.get("client").toString();
            this.service = config.get("client").toString();
        }else if("validation".equals(type)){
            this.serverName = config.get("casServerTicket").toString();
            this.service = config.get("client").toString();
        }
        return CommonUtils.constructServiceUrl(request, response, this.service, this.serverName, this.artifactParameterName, this.encodeServiceUrl);
    }

6)、由于该方法在其他几处也有调用,故而需要对其他调用的地方进行一一修改,但其他调用处最后Type的参数都统一修改成“validation”,“auth”仅上述AuthenticationFilte方法中的doFilter中使用,具体涉及到的类分别为:AbstractTicketValidationFilter中doFilter方法第169和184行constructServiceUrl方法的调用,增加“validation”参数即可,同时测试类CasFilterTests中也需要进行修改,参数同样为“validation”,具体代码如下:

constructServiceUrl(request, response,"validation")

到此已完成了CAS-Client-core源代码的修改.

7)、完成上述源代码的增加与修改后,进行编译打成cas-client-core- 3.2.1.jar包,分别放置于cas工程以及调用cas的其他WEB应用工程中,同时需要在调用cas的WEB应用工程中增加两个配置文件,分别为: cas-service.propertiessegment.properties

7.1cas-service.properties配置文件内容如下:

#cas服务端的内网和外网

cas.inCasServer=http://10.206.20.52:8982/cas

cas.outCasServer=http://218.6.169.98:18982/cas



#调用cas的其他WEB应用的内网和外网

cas.inClient=http://10.206.20.52:8982/tickets

cas.outClient=http://218.6.169.98:18982/tickets



#cas服务端的内网和外网(casTicket)

cas.inCasServerTicket=http://10.206.20.52:8982/cas

cas.outCasServerTicket=http://218.6.169.98:18982/cas

2segment.properties配置文件内容如下:

#网段

segment_1 =10.0.0.0-10.255.255.255

segment_2 =172.16.0.0-172.31.255.255

segment_3 =192.168.0.0-192.168.255.255

segment_4 =172.10.0.0-172.31.255.255

注:此处网段只需要配置内网的限制IP,也就是出现在此配置文件中的内网IP才可以访问,外网无须配置;另WEB.xml配置的cas服务地址将不再有效,处于失效状态,而是通过这两个配置文件进行读取,但web.xml配置的信息依然需要保留。

2、内外网映射端口丢失

犹豫nginx重定向问题,容易把url端口号省略,故在nginx.conf配置中,需要重定向配置,内容如下:

location / {
            port_in_redirect on;
            #外网
            proxy_redirect http://183.129.XXX.XXX/ http://183.129.XXX.XXX:1280/;
            #内网
            proxy_redirect http://192.168.1.XXX/ http://192.168.1.XXX:80/;
            proxy_pass http://myserver;
            proxy_next_upstream http_404 non_idempotent http_500 error timeout http_502;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

            index  index.html index.htm;
            root   html;
        }

你可能感兴趣的:(CAS,内外网映射)