弹簧套2.2.5
shiro弹簧靴网络启动器1.5.1
buji-pac4j 4.1.1
pac4j cas 3.8.3
cas覆盖模板5.3。
我用https在tomcat中启动cas服务器,并在eclipse中启动两个客户端(pac4j1
和pac4j2
(。
单点登录有效,但single sign out
失败。
以下是我的配置:
我只在cas服务器下添加了一个service
文件,看起来像:
{
"@class": "org.apereo.cas.services.RegexRegisteredService",
"serviceId": "^(http)://localhost.*",
"name": "local",
"id": 10000003,
"evaluationOrder": 1
}
pac4j1
:的应用程序yml
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配置:
@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;
}
}
默认cas server
的application.properties
,cas server
使用https(https://localhost:8443/cas
(,cas clients
使用http(http://localhost:8444/pac4j1
(。
我哪里错了
在leopal
提供的链接SLO的帮助下,我知道cas服务器需要向客户端发送注销请求。
因此,我查看了cas服务器的日志,找到了INFO [org.apereo.cas.logout.DefaultLogoutManager] - <Performing logout operations for
。
所以我添加了org.apereo.cas.logout
的日志,发现有一些关于注销的类:DefaultLogoutManager
、DefaultSingleLogoutServiceLogoutUrlBuilder
、DefaultSingleLogoutServiceMessageHandler
和SimpleUrlValidator
。
当执行注销时,DefaultSingleLogoutServiceLogoutUrlBuilder.determineLogoutUrl
将从注册服务中获取注销url,或者如果原始url是有效url,则从cas客户端获取原始url。
所以我的问题是:我没有在服务json文件中定义注销url,来自cas客户端的原始url是localhost:8444
,这是一个无效的ipv4。因此,cas服务器不会将注销请求发送回客户端。
解决方案是:在项目url中使用ip
,而不是cas客户端的application.yml
中的localhost
:
cas:
client-name: pac4j1Client
server:
url: https://localhost:8443/cas
project:
url: http://192.168.2.119:8444/pac4j1
另一个解决方案是为每个cas客户端服务json文件设置CCD_ 24(尚未尝试(。