一般项目绝大数都是部署于某一个网络,要吗在内网,要不在外网,部署在内网的目前很多都通过VPN进行内网的访问。但对于一些项目是部署在内网,然后通过网络路由映射方式进行外网的访问,一般情况如果是通过自己开发的登陆此问题不需要进行任何改动,而当您使用了通用的CAS统一认证服务时,由于WEB应用工程中web.xml配置的CAS地址是固定的,而不是一个动态的地址,当将WEB应用服务器例如TOMCAT端口映射外网后,在访问应用时会自动根据在web.xml文件中去配置对应的CAS地址,而此时的地址只能是内网使用,外网自然无法找到,则无法登陆,而由于项目的本身需要,必须要同时内外网都能访问,而应用已使用了CAS再来变更带来一些不便,同时CAS统一认证方面安全性还是较为有优势的,根据这一情况,前期进行了大量的咨询,都没能找得很好的解决办法,对于公司内部也无人能够解决,后通过百度搜索到了大量信息,并进行了测试,发现很多都无法满足自己的需要,后在无意中发现CNBlogs的一位作者名为BetterFuture的在自己博文中对CAS内外网功能的一个介绍,但由于没有进行全部的解说和一些代码的问题,并未能成功解决,后联系上了作者,并在其指导下完成自己的项目内网外功能,在此本人根据自己项目的情况进行了一次总结,以便于帮助更多的人;本案例以CAS-Client-3.2.1为例进行说明,对应的服务端为CAS-Server-3.5.2,目前客户端最新的为CAS-Client-3.3.3,对应CAS-Server-4.0以上,其不同版本源码虽有所改变,但此方法仍适用,可以直接用于CAS-Client-3.3.3版本。
第一步,首先要先下载cas-client-3.2.1-release.zip源文件,使用压缩软件打开,其中cas-client-core即为cas-client-core-3.2.1.jar的源代码,其下载地址为:http://developer.jasig.org/cas-clients/
第二步,将解压的cas-client-core目录导入到Eclipse或MyEclipse工程中,建立JavaProject,生成.project和.classpath文件,即可开始源代码的修改工作,目前这些源代码工程都是使用Maven来构建的,关于Maven不在本文范畴故而不再赘述!
第三步,修改源代码,新增工具类,以实现内外网的访问,当完成上述导入后,在Eclipse或MyEclipse中的目录结构应如下:
第四步,在org.jasig.cas.client.util下继续新增工具类PropertiesUtil代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
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
Map 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; } } |
第五步,在org.jasig.cas.client.util下继续新增工具类HttpConnectionUtil代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
package org.jasig.cas.client.util;
import java.net.HttpURLConnection; import java.net.URL; import java.util.Map;
public class HttpConnectionUtil {
/** * 测试网络是否能连接通畅 * * @param serviceURL * @return */ public static boolean isConnection(String serviceURL) { try { URL url = new URL(serviceURL); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(2000);// 2秒则超时 conn.setReadTimeout(2000); int state = conn.getResponseCode(); if (state == 200) { return true; } } catch (Exception e) { return false; } return false; }
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; }
public static void main(String[] args) { if (ipIsValid("127.0.0.1-127.0.0.1", "127.0.0.1")) { System.out.println("ip属于该网段"); } else{ System.out.println("ip不属于该网段"); } }
/** * 获取配置文件信息 * * @return */ public static Map return PropertiesUtil.readProperties(HttpConnectionUtil.class .getResource("/").getPath() + "cas-service.properties"); }
|
第六步,在org.jasig.cas.client.util下继续新增工具类CustomConfigUtil,代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
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 final ServletContext servletContext, final HttpServletRequest request) { Map Map //读取配置文件 Map boolean falg = false; for (String key : segmentMap.keySet()) { //判断是否属于某个网段 falg = HttpConnectionUtil.ipIsValid(segmentMap.get(key), request.getRemoteAddr()); 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服务器地址 boolean flag = HttpConnectionUtil.isConnection(map2.get("cas.outCasServerTicket")); if(flag){ map.put("casServerTicket", map2.get("cas.outCasServerTicket")); }else{ map.put("casServerTicket", map2.get("cas.inCasServerTicket")); } } return map; }
} |
第七步,完成上述源代码的增加工具类后,开始修改CAS中的源代码了,首先修改AuthenticationFilte ,添加静态属性:
public static final String CONST_CAS_GATEWAY = "_const_cas_gateway_";
然后找到方法:doFilter方法内容修改为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public final void doFilter(final ServletRequest servletRequest,final ServletResponse servletResponse,final FilterChain filterChain)throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest)servletRequest; final HttpServletResponse response = (HttpServletResponse)servletResponse; final HttpSession session = request.getSession(false); final String ticket = request.getParameter(getArtifactParameterName()); final Assertion assertion = session !=null ? (Assertion)session.getAttribute(CONST_CAS_ASSERTION) :null; boolean wasGatewayed = (session !=null) && (session.getAttribute(CONST_CAS_GATEWAY) !=null); /**以下部分为主要修改内容,具体可参照进行修改**/ if ((CommonUtils.isBlank(ticket)) && (assertion ==null) && (!wasGatewayed)){ this.log.debug("noticket and no assertion found"); if (this.gateway){ this.log.debug("settinggateway attribute in session"); request.getSession(true).setAttribute(CONST_CAS_GATEWAY,"yes"); } String serviceUrl = constructServiceUrl(request, response,"auth");
Map this.casServerLoginUrl = ((String)config.get("casServerTicketUrl")).toString(); setCasServerLoginUrl(((String)config.get("casServerTicketUrl")).toString()); String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), serviceUrl,this.renew,this.gateway); if (this.log.isDebugEnabled()){ this.log.debug("redirectingto \"" + urlToRedirectTo +"\""); }
final String modifiedServiceUrl; log.debug("no ticket and no assertion found"); if (this.gateway) { log.debug("setting gateway attribute in session"); modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl); } else { modifiedServiceUrl = serviceUrl; } if (log.isDebugEnabled()){ log.debug("Constructed service url: " + modifiedServiceUrl); } response.sendRedirect(urlToRedirectTo); return; }
if (session !=null){ this.log.debug("removinggateway attribute from session"); session.setAttribute(CONST_CAS_GATEWAY,null); } filterChain.doFilter(request, response); } |
第八步,由于修改了constructServiceUrl这个方法,增加了一个参数,由原来该方法只有两个参数 现在修改为3个,需要找到该方法的类AbstractCasFilter(源代码的第 113或114行中的constructServiceUrl方法)进行修改重载该方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
protected final String constructServiceUrl(final HttpServletRequest request,final HttpServletResponse response,final String type) { //从配置文件中取出cas服务器的登陆地址 Map 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); } |
第九步,由于该方法在其他几处也有调用,故而需要对其他调用的地方进行一一修改,但其他调用处最后Type的参数都统一修改成“validation”,“auth”仅上述AuthenticationFilte方法中的doFilter中使用,具体涉及到的类分别为:AbstractTicketValidationFilter中doFilter方法第169和184行constructServiceUrl方法的调用,增加“validation”参数即可,同时测试类CasFilterTests中也需要进行修改,参数同样为“validation”,具体代码如下:
constructServiceUrl(request, response,"validation")
到此已完成了CAS-Client-core源代码的修改.
第十步,完成上述源代码的增加与修改后,进行编译打成cas-client-core- 3.2.1.jar包,分别放置于cas工程以及调用cas的其他WEB应用工程中,同时需要在调用cas的WEB应用工程中增加两个配置文件(放置于web-inf/classes目录下),分别为: cas-service.properties与segment.properties。
1、cas-service.properties配置文件内容如下:
#cas服务端的内网和外网
cas.inCasServer=http://内网ip地址/cas
cas.outCasServer=http://外网ip地址/cas
#调用cas的其他WEB应用的内网和外网
cas.inClient=http://内网ip地址/tickets
cas.outClient=http://外网ip地址/tickets
#cas服务端的内网和外网(casTicket)
cas.inCasServerTicket=http://内网ip地址/cas
cas.outCasServerTicket=http://外网ip地址/cas
2、segment.properties配置文件内容如下:
#网段
segment_1 =内网网段区间1
segment_2 =内网网段区间2
segment_3 =内网网段区间3
segment_4 =内网网段区间4
注:此处网段只需要配置内网的限制IP,也就是出现在此配置文件中的内网IP才可以访问,外网无须配置;另WEB.xml配置的cas服务地址将不再有效,处于失效状态,而是通过这两个配置文件进行读取,但web.xml配置的信息依然需要保留。