公司的springboot框架适配客户的单点登录服务器时遇到一些问题,记录下来方便自己以后参考。
- 客户提供的单点登录demo是适用于web.xml配置的样例
Welcome to Tomcat
Welcome to Tomcat
CAS Single Sign Out Filter
org.jasig.cas.client.session.SingleSignOutFilter
CAS Single Sign Out Filter
/*
org.jasig.cas.client.session.SingleSignOutHttpSessionListener
CASFilter
org.jasig.cas.client.authentication.AuthenticationFilter
casServerLoginUrl
http://xxx.xxx.xxx.xxx:xxxx/cas/login
renew
false
gateway
false
serverName
http://xxx.xxx.xxx.xxx:xxxx/
CASFilter
/loginsso.jsp
CAS Validation Filter
org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
casServerUrlPrefix
http://xxx.xxx.xxx.xxx:xxxx//cas
serverName
http://xxx.xxx.xxx.xxx:xxxx/
useSession
true
redirectAfterValidation
true
CAS Validation Filter
/loginsso.jsp
CAS HttpServletRequest Wrapper Filter
org.jasig.cas.client.util.HttpServletRequestWrapperFilter
CAS HttpServletRequest Wrapper Filter
/loginsso.jsp
CAS Assertion Thread Local Filter
org.jasig.cas.client.util.AssertionThreadLocalFilter
CAS Assertion Thread Local Filter
/loginsso.jsp
setnameFilter
com.loginsso.ClientFilter
uumsServiceUrl
http://xxx.xxx.xxx.xxx:xxxx//UUMS/services/UUMSService
appName
admin
setSessionClass
com.loginsso.LoginSetSession
setnameFilter
/loginsso.jsp
- 将客户所提供的demo中的filter以springboot的方式加载
package com;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.jasig.cas.client.util.AssertionThreadLocalFilter;
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Configuration;
import com.lyarc.corp.filter.AuthenticationFilter;
import com.lyarc.corp.filter.ClientFilter;
import com.lyarc.engine.utils.SpringUtil;
@Configuration
public class MyWebApplicationInitializer implements ServletContextInitializer {
private static final String mp="/count/list";
//private static final String casServer="http://xxx.xxx.xxx.xxx:xxxx";
//private static final String serverName="http://xxx.xxx.xxx.xxx:xxxx";
//本地环境模拟地址,发布时用上面一组配置
private static final String casServer="http://xxx.xxx.xxx.xxx:xxxx";
private static final String serverName="http://xxx.xxx.xxx.xxx:xxxx";
//private static final String serverName="http://xxx.xxx.xxx.xxx:xxxx";
@Override
public void onStartup(ServletContext sc) {
//单点登出
FilterRegistration.Dynamic cASLogoutFilter = sc.addFilter("CAS Single Sign Out Filter", SingleSignOutFilter.class);
cASLogoutFilter.addMappingForUrlPatterns(null, false, "/*");
sc.addListener(SingleSignOutHttpSessionListener.class);
//单点登录
FilterRegistration.Dynamic cASLoginFilter = sc.addFilter("CASFilter", AuthenticationFilter.class);
//CAS login 服务地址
cASLoginFilter.setInitParameter("casServerLoginUrl", casServer+"/cas/login");
cASLoginFilter.setInitParameter("renew", "false");
cASLoginFilter.setInitParameter("gateway", "false");
//客户端应用服务地址
cASLoginFilter.setInitParameter("serverName", serverName);
cASLoginFilter.setInitParameter("ignoreInterface", "xxx/xxx/xxx");
//此处修改成XX系统新增的跳转页面
cASLoginFilter.addMappingForUrlPatterns(null, false, mp);
//负责Ticket校验
FilterRegistration.Dynamic cASValidationFilter = sc.addFilter("CAS Validation Filter", Cas20ProxyReceivingTicketValidationFilter.class);
cASValidationFilter.setInitParameter("casServerUrlPrefix", casServer+"/cas");
cASValidationFilter.setInitParameter("serverName", serverName);
cASValidationFilter.setInitParameter("useSession", "true");
cASValidationFilter.setInitParameter("redirectAfterValidation", "true");
//此处修改成XX系统新增的跳转页面
cASValidationFilter.addMappingForUrlPatterns(null, false, mp);
FilterRegistration.Dynamic cASWrapperFilter = sc.addFilter("CAS HttpServletRequest Wrapper Filter", HttpServletRequestWrapperFilter.class);
//此处修改成XX系统新增的跳转页面
cASWrapperFilter.addMappingForUrlPatterns(null, false, "/xxx/xxx");
FilterRegistration.Dynamic cASThreadLocalFilter = sc.addFilter("CAS Assertion Thread Local Filter", AssertionThreadLocalFilter.class);
//此处修改成XX系统新增的跳转页面
cASThreadLocalFilter.addMappingForUrlPatterns(null, false, mp);
//负责Ticket校验
FilterRegistration.Dynamic cASSetName = sc.addFilter("setnameFilter", ClientFilter.class);
cASSetName.setInitParameter("uumsServiceUrl", casServer+"/UUMS/services/UUMSService");
cASSetName.setInitParameter("appName", "xxx");
cASSetName.setInitParameter("setSessionClass", "xxx.xxx.xxx.xxx.LoginSetSession");
//此处修改成XX系统新增的跳转页面
cASSetName.addMappingForUrlPatterns(null, false, mp);
}
}
也可以通过bean的方式加载filter
@Bean
public FilterRegistrationBean filterRegistrationSingleSignOutFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new SingleSignOutFilter());
registration.addUrlPatterns("/*");
registration.setOrder(1);
return registration;
}
@Bean
public FilterRegistrationBean filterRegistrationAuthenticationFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new AuthenticationFilter());
//CAS login 服务地址
registration.addInitParameter("casServerLoginUrl", "http://xxx.xxx.xxx.xxx:xxx/cas/login");
registration.addInitParameter("renew", "false");
registration.addInitParameter("gateway", "false");
//客户端应用服务地址
registration.addInitParameter("serverName", "http://xxx.xxx.xxx.xxx:xxx");
//此处修改成XX系统新增的跳转页面
registration.addUrlPatterns("/xxx/xxx");
return registration;
}
- 修改 AuthenticationFilter 及LoginSetSession 适配单点登录流程
- 页面请求业务系统时,AuthenticationFilter 判断session中是否有登陆用户,如果有则执行正常业务流程,如果没有,则跳转到CAS服务器验证登录,
- CAS服务器验证后,将请求重定向到业务系统,并附带返回票根信息(ticket)
- 业务系统收到请求后,使用Cas20ProxyReceivingTicketValidationFilter先验证对票根信息进行验证是否是有效服务目录返回的,无误后放行进入ClientFilter处理
- ClientFilter接收到ticket后,再次请求CAS服务器,通过ticket,获取用户的身份识别码并在本地将对应识别码的用户加载到session中,完成登录
上述流程适配过程中出现一个非常诡异的问题,第一次单点登录时,业务系统在使用Cas20ProxyReceivingTicketValidationFilter校验ticket时,会报一个票根'ST-xxxxxx-cas'不符合目标服务”的错误,再次刷新页面(第二次访问触发单点流程)一切正常。
这个错误一般都是由于业务系统配置地址与实际访问地址不符时才会出现,但确认配置没有问题。最后发现是我们的springboot业务系统在配置文件里对session使用的cookie名字进行定制造成的,取消这个配置,单点登录就恢复正常。
session:
cookie:
name: xxx-xxx-sessionid
初步猜测单点登录是依赖浏览器端的cookie来维持跨系统的登录状态,使用的是默认jsession,如果被重新定义名称的话,回调时session解析出了问题。