最近项目分给我一个需求解决CAS认证登陆的内外网双IP访问的问题,当使用通用的CAS统一认证服务时,由于WEB应用工程中web.xml配置的CAS地址是固定的,而不是一个动态的地址,当将WEB应用服务器例如TOMCAT端口映射外网后,在访问应用时会自动根据在web.xml文件中去配置对应的CAS地址,而此时的地址只能是内网使用,外网自然无法找到,则无法登陆,而由于项目的本身需要,必须要同时内外网都能访问,由此看了一周源码后,提供以下解决方案。
供工具类
public class HttpConnectionUtil {
//读取配置文件信息
static Properties prop = new Properties();
static{
InputStream inStream = HttpConnectionUtil.class.getClassLoader().getResourceAsStream("application.properties");
try {
prop.load(inStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
*
* @param name
* @return
*/
//在配置文件中通过键取值
public static String getByName(String name){
return prop.getProperty(name);
}
//判断是内网环境还是外网环境
public static boolean isInner(String clientIP) {
String reg = "(10|172|192|127)\\.([0-1][0-9]{0,2}|[2][0-5]{0,2}|[3-9][0-9]{0,1})\\.([0-1][0-9]{0,2}|[2][0-5]{0,2}|[3-9][0-9]{0,1})\\.([0-1][0-9]{0,2}|[2][0-5]{0,2}|[3-9][0-9]{0,1})";
Pattern p = Pattern.compile(reg);
Matcher matcher = p.matcher(clientIP);
return matcher.find();
}
}
源码解读:
本项目是cas和shiro的整合,cas认证登陆加入到shiro的过滤连里面:
在web.xml里面配置:
<filter>
<filter-name>shiroFilterfilter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
<init-param>
<param-name>targetFilterLifecycleparam-name>
<param-value>trueparam-value>
init-param>
<init-param>
<param-name>targetBeanNameparam-name>
<param-value>shiroFilterparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>shiroFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
然后过滤链在spring容器里面找id为shiroFilter的bean工厂
<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
<property name="failureUrl" value="${common_in.ip}/disrec" />
bean>
<bean id="casRealm" class="com.zonekey.disrec.service.auth.ShiroDbRealm">
<property name="cachingEnabled" value="true" />
<property name="authenticationCachingEnabled" value="true" />
<property name="authenticationCacheName" value="authenticationCache" />
<property name="authorizationCachingEnabled" value="true" />
<property name="authorizationCacheName" value="authorizationCache" />
<property name="casServerUrlPrefix" value="${login_in.ip}" />
<property name="casService" value="${common_in.ip}/disrec/shiro-cas" />
bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="casRealm"/>
<property name="rememberMeManager" ref="rememberMeManager" />
<property name="subjectFactory" ref="casSubjectFactory"/>
bean>
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cipherKey"
value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}" />
<property name="cookie" ref="rememberMeCookie" />
bean>
<bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" />
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="sid" />
<property name="httpOnly" value="true" />
<property name="maxAge" value="-1" />
bean>
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe" />
<property name="httpOnly" value="true" />
<property name="maxAge" value="2592000" />
bean>
<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<property name="activeSessionsCacheName" value="shiro-activeSessionCache" />
<property name="sessionIdGenerator" ref="sessionIdGenerator" />
bean>
<bean id="logout" class="org.apache.shiro.web.filter.authc.LogoutFilter">
<property name="redirectUrl" value="${login_in.ip}/cas/logout?service=${common_in.ip}/disrec/shiro-cas/" />
bean>
<bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory"/>
**//这个就是我们要改源码的地方**
<bean id="formAuthenticationFilter" class="com.zonekey.disrec.common.utils.redirectIP.MyFormAuthenticationFilter" />
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager" />
<property name="arguments" ref="securityManager" />
bean>
一般是不需要改动源码,只是用子类继承基类能达到改动效果
package com.zonekey.disrec.common.utils.redirectIP;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.cas.CasFilter;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.zonekey.disrec.service.auth.ShiroDbRealm;
public class MyFormAuthenticationFilter extends FormAuthenticationFilter{
/**
* loginUrl地址重写
*/
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
HttpServletRequest req=(HttpServletRequest)request;
HttpServletResponse res=(HttpServletResponse)response;
//动态获取请求服务端的地址
String serverIp=req.getRequestURL().toString();
//请求资源
String serverAddr=req.getRequestURI();
//动态截取请求服务IP
String commonIp=serverIp.substring(0,serverIp.indexOf(serverAddr));
//System.out.println("commonIP:"+commonIp);
//String common_in = ReadProperties.getByName("common_in.ip");
String login_in = HttpConnectionUtil.getByName("login_in.ip");
if(login_in==null) login_in= commonIp+"/cas";
//String common_out = ReadProperties.getByName("common_out.ip");
String login_out = HttpConnectionUtil.getByName("login_out.ip");
if(login_out==null) login_out= commonIp+"/cas";
//获取servletContext容器
ServletContext sc=req.getSession().getServletContext();
//获取web环境下spring容器
ApplicationContext ac=WebApplicationContextUtils.getWebApplicationContext(sc);
//ApplicationContextUtil ac=new ApplicationContextUtil();
ShiroDbRealm shiroDbRealm=(ShiroDbRealm)ac.getBean("casRealm");
LogoutFilter logoutFilter=(LogoutFilter)ac.getBean("logout");
//一一一一一一一一一一一注意是此处一一一一一一一一一一一一一一一一一一一
ShiroFilterFactoryBean shiroFilter=(ShiroFilterFactoryBean) ac.getBean("&shiroFilter");
String clientIP=null;
if (req.getHeader("x-forwarded-for") == null) {
clientIP=req.getRemoteAddr();
}else{
clientIP=req.getHeader("x-forwarded-for");
}
/*System.out.println("clientIp:"+clientIP);
System.out.println("isInner:"+HttpConnectionUtil.isInner(clientIP));*/
if(!HttpConnectionUtil.isInner(clientIP)){
shiroFilter.setLoginUrl(login_out+"/login?service="+commonIp+req.getContextPath()+"/shiro-cas");
//casFilter.setFailureUrl(map.get("common_out.ip")+"/disrec");
//shiroDbRealm.setCasServerUrlPrefix(login_out);
shiroDbRealm.setCasService(commonIp+req.getContextPath()+"/shiro-cas");
logoutFilter.setRedirectUrl(login_out+"/logout?service="+commonIp+req.getContextPath()+"/shiro-cas");
//二二二${common_out.ip}/sysmanagement/shiro-cas
}else{
shiroFilter.setLoginUrl(login_in+"/login?service="+commonIp+req.getContextPath()+"/shiro-cas");
//casFilter.setFailureUrl(map.get("common_in.ip")+"/disrec");
//shiroDbRealm.setCasServerUrlPrefix(login_in);
shiroDbRealm.setCasService(commonIp+req.getContextPath()+"/shiro-cas");
logoutFilter.setRedirectUrl(login_in+"/logout?service="+commonIp+req.getContextPath()+"/shiro-cas");
}
WebUtils.issueRedirect(req, res, shiroFilter.getLoginUrl());
}
}
由此双IP问题得到了解决
整个思路:client发起请求,截取到client的请求路径判断用户IP是内网还是外网访问,然后再认证成功重定向路径动态重写登陆成功跳转的路径。