Reputation: 23
I've been working on a Spring Boot app that uses CAS for SSO for days now. Single sign on works great! And I have a logout button within the app that works as well. The problem I'm running in to is Single Logout (SLO) does not work. Meaning when I log out of another application, SLO works everywhere except for this app. When I log out of this app, though, I am logged out of everything else. So it seems like this app here isn't listening for when an SSO session is ended.
Our CAS is version 6.5, and I'm using spring-security-cas version 5.7.2.
Here's my WebSecurityConfig:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class);
private SingleSignOutFilter singleSignOutFilter;
private LogoutFilter logoutFilter;
private CasAuthenticationProvider casAuthenticationProvider;
private ServiceProperties serviceProperties;
@Autowired
public WebSecurityConfig(SingleSignOutFilter singleSignOutFilter, LogoutFilter logoutFilter,
CasAuthenticationProvider casAuthenticationProvider,
ServiceProperties serviceProperties) {
this.logoutFilter = logoutFilter;
this.singleSignOutFilter = singleSignOutFilter;
this.serviceProperties = serviceProperties;
this.casAuthenticationProvider = casAuthenticationProvider;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable(); //for testing
http.authorizeRequests()
.antMatchers("/login/cas","/styles.css").permitAll()
.antMatchers("/profile").hasAuthority(appAdmin)
.antMatchers( "/**").hasAnyAuthority(appUser)
.and()
.addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class)
.addFilterBefore(logoutFilter, LogoutFilter.class)
.exceptionHandling().accessDeniedPage("/403")
.authenticationEntryPoint(authenticationEntryPoint()
);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(casAuthenticationProvider);
}
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return new ProviderManager(Collections.singletonList(casAuthenticationProvider));
}
public AuthenticationEntryPoint authenticationEntryPoint() {
CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();
entryPoint.setLoginUrl(casServer+"/cas/login");
entryPoint.setServiceProperties(serviceProperties);
return entryPoint;
}
}
And here's my Cas App:
@EntityScan
@SpringBootApplication ()
public class CasSecuredApplication extends SpringBootServletInitializer {
private static final Logger logger = LoggerFactory.getLogger(CasSecuredApplication.class);
public static void main(String... args) {
SpringApplication.run(CasSecuredApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(CasSecuredApplication.class);
}
@Bean
public CasAuthenticationFilter casAuthenticationFilter(
AuthenticationManager authenticationManager,
ServiceProperties serviceProperties) throws Exception {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
filter.setServiceProperties(serviceProperties);
return filter;
}
@Bean
public ServiceProperties serviceProperties() {
logger.info("service properties");
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(appServer+contextPath+"/login/cas");
serviceProperties.setSendRenew(false);
return serviceProperties;
}
@Bean
public TicketValidator ticketValidator() {
return new Cas30ServiceTicketValidator(casServer+"/cas/") ;}
@Autowired
private AuthenticationUserDetailsService UserDetail;
@Bean
public CasAuthenticationProvider casAuthenticationProvider(
TicketValidator ticketValidator,
ServiceProperties serviceProperties) {
CasAuthenticationProvider provider = new CasAuthenticationProvider();
provider.setServiceProperties(serviceProperties);
provider.setTicketValidator(ticketValidator);
provider.setAuthenticationUserDetailsService(UserDetail);
provider.setKey("CAS_PROVIDER_LOCALHOST_8900")
;
return provider;
}
@Bean
public SecurityContextLogoutHandler securityContextLogoutHandler() {
return new SecurityContextLogoutHandler();
}
@Bean
public LogoutFilter logoutFilter() {
LogoutFilter logoutFilter = new LogoutFilter(casServer+"/cas/logout", securityContextLogoutHandler());
logoutFilter.setFilterProcessesUrl("/logout/cas");
return logoutFilter;
}
@Bean
public SingleSignOutFilter singleSignOutFilter() {
SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
singleSignOutFilter.setLogoutCallbackPath("/exit/cas");
singleSignOutFilter.setIgnoreInitConfiguration(true);
return singleSignOutFilter;
}
@EventListener
public SingleSignOutHttpSessionListener singleSignOutHttpSessionListener(HttpSessionEvent event) {
return new SingleSignOutHttpSessionListener();
}
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
};
return tomcat;
}
}
Upvotes: 0
Views: 483
Reputation: 513
If CAS is configured for Single Log Out (SLO) you need to register this service for SLO and specify if it is a Back Channel Logout or a Front Channel Logout.
Then in the service you need to expose an endpoint (Back Channel) or a page (Front Channel) that will kill the session. When the user is directed to CAS for logout it will fire logout requests.
https://apereo.github.io/cas/6.5.x/installation/Logout-Single-Signout.html
Upvotes: 0