spring boot 2.2.5
shiro-spring-boot-web-starter 1.5.1
buji-pac4j 4.1.1
pac4j-cas 3.8.3
cas overlay template 5.3.
I start cas server in tomcat with https, and start two clients(pac4j1
and pac4j2
) in eclipse.
single sign on works, but single sign out
failed.
Following are my configs:
I only added one service
file under cas server which looks like:
{
"@class": "org.apereo.cas.services.RegexRegisteredService",
"serviceId": "^(http)://localhost.*",
"name": "local",
"id": 10000003,
"evaluationOrder": 1
}
application.yml of pac4j1
:
server:
port: 8444
servlet:
context-path: /pac4j1
cas:
client-name: pac4j1Client
server:
url: https://localhost:8443/cas
project:
url: http://localhost:8444/pac4j1
Pac4jConfig:
@Configuration
public class Pac4jConfig {
@Value("${cas.server.url}")
private String casServerUrl;
@Value("${cas.project.url}")
private String projectUrl;
@Value("${cas.client-name}")
private String clientName;
@Bean("authcConfig")
public Config config(CasClient casClient, ShiroSessionStore shiroSessionStore) {
Config config = new Config(casClient);
config.setSessionStore(shiroSessionStore);
return config;
}
@Bean
public ShiroSessionStore shiroSessionStore(){
return new ShiroSessionStore();
}
@Bean
public CasClient casClient(CasConfiguration casConfig){
CasClient casClient = new CasClient(casConfig);
casClient.setCallbackUrl(projectUrl + "/callback?client_name=" + clientName);
casClient.setName(clientName);
return casClient;
}
@Bean
public CasConfiguration casConfig(){
final CasConfiguration configuration = new CasConfiguration();
configuration.setLoginUrl(casServerUrl + "/login");
configuration.setProtocol(CasProtocol.CAS20);
configuration.setAcceptAnyProxy(true);
configuration.setPrefixUrl(casServerUrl + "/");
return configuration;
}
}
shiro config:
@Configuration
public class ShiroConfig {
@Value("${cas.project.url}")
private String projectUrl;
@Value("${cas.server.url}")
private String casServerUrl;
@Value("${cas.client-name}")
private String clientName;
@Bean("securityManager")
public DefaultWebSecurityManager securityManager(Pac4jSubjectFactory subjectFactory, CasRealm casRealm){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(casRealm);
manager.setSubjectFactory(subjectFactory);
return manager;
}
@Bean
public CasRealm casRealm(){
CasRealm realm = new CasRealm();
realm.setClientName(clientName);
realm.setCachingEnabled(false);
realm.setAuthenticationCachingEnabled(false);
realm.setAuthorizationCachingEnabled(false);
return realm;
}
@Bean
public Pac4jSubjectFactory subjectFactory(){
return new Pac4jSubjectFactory();
}
@Bean
public FilterRegistrationBean<SingleSignOutFilter> singleSignOutFilter() {
FilterRegistrationBean<SingleSignOutFilter> bean = new FilterRegistrationBean<SingleSignOutFilter>();
bean.setName("singleSignOutFilter");
SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
singleSignOutFilter.setCasServerUrlPrefix(casServerUrl);
singleSignOutFilter.setIgnoreInitConfiguration(true);
bean.setFilter(singleSignOutFilter);
bean.addUrlPatterns("/*");
bean.setEnabled(true);
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
@Bean
public FilterRegistrationBean<DelegatingFilterProxy> filterRegistrationBean() {
FilterRegistrationBean<DelegatingFilterProxy> filterRegistration = new FilterRegistrationBean<DelegatingFilterProxy>();
filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
filterRegistration.addInitParameter("targetFilterLifecycle", "true");
filterRegistration.setEnabled(true);
filterRegistration.addUrlPatterns("/*");
filterRegistration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.FORWARD);
return filterRegistration;
}
private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean){
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/", "securityFilter");
filterChainDefinitionMap.put("/index", "securityFilter");
filterChainDefinitionMap.put("/callback", "callbackFilter");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/**","anon");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager, Config config) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
loadShiroFilterChain(shiroFilterFactoryBean);
Map<String, Filter> filters = new HashMap<>(3);
SecurityFilter securityFilter = new SecurityFilter();
securityFilter.setConfig(config);
securityFilter.setClients(clientName);
filters.put("securityFilter", securityFilter);
MyCallbackFilter callbackFilter = new MyCallbackFilter();
callbackFilter.setConfig(config);
callbackFilter.setDefaultUrl(projectUrl);
filters.put("callbackFilter", callbackFilter);
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setConfig(config);
logoutFilter.setCentralLogout(true);
logoutFilter.setLocalLogout(true);
logoutFilter.setDefaultUrl(projectUrl + "/callback?client_name=" + clientName);
filters.put("logout",logoutFilter);
shiroFilterFactoryBean.setFilters(filters);
return shiroFilterFactoryBean;
}
}
application.properties
of cas server
is default, and cas server
use https(https://localhost:8443/cas
) while cas clients
are http(http://localhost:8444/pac4j1
).
Where am I wrong?
with the help of the link SLO which provided by leopal
, i know that cas server need to send log out request back to client.
Hence, i checked the log of cas server and found INFO [org.apereo.cas.logout.DefaultLogoutManager] - <Performing logout operations for
.
so i added log for org.apereo.cas.logout
and found that there are some classes about logout: DefaultLogoutManager
, DefaultSingleLogoutServiceLogoutUrlBuilder
, DefaultSingleLogoutServiceMessageHandler
and SimpleUrlValidator
.
when performing logout, DefaultSingleLogoutServiceLogoutUrlBuilder.determineLogoutUrl
will get the logout url from registered service or get the original url from cas client if original url is a valid url.
So my problem is : i didn't define logout url in service json file and the original url from cas client is localhost:8444
which is a invalid ipv4. As a result, cas server will not send logout request back to client.
Solution is : use ip
in project url instead of localhost
in application.yml
of cas client:
cas:
client-name: pac4j1Client
server:
url: https://localhost:8443/cas
project:
url: http://192.168.2.119:8444/pac4j1
another solution is set logoutUrl
for each cas client service json file(not tried yet).