由于工作需要,要对接cas单点登录,重点是对接两个平台的cas单点登录。对于这种情况的实现思路是:
在idea中对starter工程进行分拆,拆成dev-A和dev-B两个module模块。两个module都是以starter为parent工程。starter工程尽量只做公共jar包的引用。
项目中接入单点登录,分别设置如下:
(1)接入第一个平台的cas,在pom引用dev-A的jar包,且在某配置文件上对第一个平台的cas值写为true,对第二个平台的cas值写为false。
(2)接入第二个平台的cas,在pom引用dev-B的jar包,且在某配置文件上对第二个平台的cas值写为true,对第一个平台的cas值写为false。
对于接入cas来说,一般都大致相同,根据自己的情况去决定配置过滤器,常用要配置的过滤器大致有:
(1)SingleSignOutFilter:该过滤器主要配置单点登出。
(2)AuthenticationFilter:判断用户是否登录,如果登录则进入第二步,否则重定向到cas服务器。
(3)TicketValidationFilter:如果登录了,带有ticket票据,对于client接收到的ticket进行验证。
(4)HttpServletRequestWrapperFilter:对HttpServletRequest对象再包装一次,让其支持getUserPrincipal,getRemoteUser方法来取得登录的用户信息。
(5)AssertionThreadLocalFilter:是为了方便用户在应用的其它地方获取Assertion对象,其会将当前的Assertion对象存放到当前的线程变量中,那么以后用户在程序的任何地方都可以从线程变量中获取当前Assertion,无需再从Session或request中进行解析。该线程变量是由AssertionHolder持有的,我们在获取当前的Assertion时也只需要通过AssertionHolder的getAssertion()方法获取即可,如:Assertion assertion = AssertionHolder.getAssertion();
如果非springboot的项目,在配置过滤器相对还是比较容易的,只要在web.xml中添加各个过滤器就好。
但是在springboot项目就要添加几个类。
以Apdapter结尾的class文件为重写了过滤器的文件。SystemProperties文件是读取System.properties文件内容。
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Created by zhangtuo on 2017/1/12.
*/
@ConfigurationProperties(prefix = "cas", locations = "classpath:system.properties")
public class SystemProperties {
private String authenticationFilterUrl;
private String singleSignOutFilterUrl;
private String ticketValidationFilterUrl;
private String ticketValidationFilterErrorUrl;
private String ticketValidationFilterEncoding;
//省略get和set函数
}
@ConfigurationProperties注解为初始化配置文件,prefix表示配置文件中以某标签开头的,locations表示配置文件位置,声明的变量如果在配置文件中配置了值,则可以获得这个值。
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 注册过滤器,相当于web.xml注册。
*
* Created by zhangtuo on 2017/1/10.
*/
@ConditionalOnProperty(prefix = "app.cas", value="enabled",havingValue = "true")
@Configuration
public class CasAutoConfig {
@Bean
public CasAutoConfig supportCas(){
return new CasAutoConfig();
}
}
初始化cas自动配置的这个bean在spring容器中,并根据app.cas的值来确定是否要开启这个bean。
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.EventListener;
/**
* Created by zhangtuo on 2017/1/10.
*/
@Configuration
@ConditionalOnBean(CasAutoConfig.class)
@AutoConfigureAfter({Cas20ProxyReceivingTicketValidationFilterAdapter.class,SingleSignOutFilterAdapter.class, AuthenticationFilterAdapter.class})
@EnableConfigurationProperties(SystemProperties.class)//自动映射一个POJO到Spring Boot配置文件(默认是application.properties文件)的属性集。
public class WebConfig {
@Autowired
private SystemProperties systemProperties;
@Bean
public FilterRegistrationBean filterRegistrationBean(SingleSignOutFilterAdapter singleSignOutFilterAdapter){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(singleSignOutFilterAdapter);
filterRegistrationBean.setEnabled(true);
filterRegistrationBean.addUrlPatterns(systemProperties.getSingleSignOutFilterUrl());
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean filterRegistrationBean2(AuthenticationFilterAdapter authenticationFilterAdapter){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(authenticationFilterAdapter);
filterRegistrationBean.setEnabled(true);
filterRegistrationBean.setMatchAfter(true);
filterRegistrationBean.addUrlPatterns(systemProperties.getAuthenticationFilterUrl());
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean filterRegistrationBean3(Cas20ProxyReceivingTicketValidationFilterAdapter cas20ProxyReceivingTicketValidationFilterAdapter){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(cas20ProxyReceivingTicketValidationFilterAdapter);
filterRegistrationBean.setEnabled(true);
filterRegistrationBean.setMatchAfter(true);
filterRegistrationBean.addInitParameter("errorUrl",systemProperties.getTicketValidationFilterErrorUrl());
filterRegistrationBean.addInitParameter("encoding",systemProperties.getTicketValidationFilterEncoding());
filterRegistrationBean.addUrlPatterns(systemProperties.getTicketValidationFilterUrl());
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean<EventListener> getDemoListener(){
ServletListenerRegistrationBean<EventListener> registrationBean
=new ServletListenerRegistrationBean<>();
registrationBean.setListener(new SingleSignOutHttpSessionListener());
return registrationBean;
}
}
@ConditionalOnBean注解是确定当前对象是否在上下文中,然后再决定是否初始化一个bean。也就是说CasAutoConfig.class如果被装配到spring容器中初始化为bean,才会初始化该类WebConfig,同理,里面的@Bean注解标签也是一样。
@AutoConfigureAfter注解是用来定义这些配置类的载入顺序。这里定义的装配类都没有通过依赖注入注入,这个注解进行载入,可以初始化。
这里补不贴入所有过滤器的配置,以单点登出的过滤器为例,主要是重写三个函数,分别是init()、doFilter()、destory()。
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.*;
import java.io.IOException;
/**
* 该过滤器用于实现单点登出功能,可选配置
*/
@Configuration
@ConditionalOnBean(CasAutoConfig.class)
@AutoConfigureAfter(CasAutoConfig.class)
public class SingleSignOutFilterAdapter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(SingleSignOutFilterAdapter.class);
private SingleSignOutFilter singleSignOutFilter;
@Override
public void destroy() {
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
singleSignOutFilter = new SingleSignOutFilter();
singleSignOutFilter.init(arg0);
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
throws IOException, ServletException {
// TODO Auto-generated method stub
singleSignOutFilter.doFilter(arg0, arg1,arg2);
}
}
其实这么多代码,都是解决springboot不在web.xml配置过滤器的问题。