无法使 CAS 单一注销与 Spring 安全一起使用
Can't make CAS Single Sign Out work with Spring Security
我没有找到任何关于在我的应用程序上实现单点退出 CAS 功能的实际指南。我在这里尝试了很多关于 SO 的答案,但是 none 有效(比如 and this)。此外,没有找到使用 Java 配置的 Spring Security+CAS 的示例,所以我对此也有点迷茫。我什至无法确定这是否是我应该使用的实际 URL,因为文档告诉我使用“/j_spring_security_logout”,这只是将我重定向到一个空白索引页,因为我如果我正常访问它,索引页面就会工作(尽管控制台显示所有正确的请求,如 JS 和 CSS)。非常感谢一些指导,因为我找不到任何使用 Java 注释的文档。提前致谢!
我的网络安全配置:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static String CAS_URL = "https://localhost:8443/cas";
private static String APP_URL = "https://localhost:8443/i9t-YM";
@Bean
public ServiceProperties serviceProperties() {
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(APP_URL+"/j_spring_cas_security_check");
serviceProperties.setSendRenew(false);
return serviceProperties;
}
@Bean
public CasAuthenticationProvider casAuthenticationProvider() {
CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
casAuthenticationProvider.setAuthenticationUserDetailsService(authenticationUserDetailsService());
casAuthenticationProvider.setServiceProperties(serviceProperties());
casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
casAuthenticationProvider.setKey("an_id_for_this_auth_provider_only");
return casAuthenticationProvider;
}
@Bean
public AuthenticationUserDetailsService authenticationUserDetailsService() {
return new TestCasAuthenticationUserDetailsService();
}
@Bean
public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
return new Cas20ServiceTicketValidator(CAS_URL);
}
@Bean
public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter.setAuthenticationManager(authenticationManager());
return casAuthenticationFilter;
}
@Bean
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
casAuthenticationEntryPoint.setLoginUrl(CAS_URL+"/login");
casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
return casAuthenticationEntryPoint;
}
@Bean
public SingleSignOutFilter SingleSignOutFilter(){
return new SingleSignOutFilter();
}
@Bean
public LogoutFilter requestLogoutFilter(){
SecurityContextLogoutHandler handler = new SecurityContextLogoutHandler();
handler.setClearAuthentication(true);
handler.setInvalidateHttpSession(true);
LogoutFilter logoutFilter = new LogoutFilter(APP_URL, handler);
return logoutFilter;
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(casAuthenticationProvider());
auth.inMemoryAuthentication().withUser("joe").password("joe").roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilter(casAuthenticationFilter());
http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint());
http.addFilterBefore(requestLogoutFilter(), LogoutFilter.class);
http.addFilterBefore(SingleSignOutFilter(), CasAuthenticationFilter.class);
http.httpBasic().and().authorizeRequests().antMatchers("/index.html", "/home.html", "/login.html", "/")
.permitAll().anyRequest().authenticated()
.and().addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.csrf().csrfTokenRepository(csrfTokenRepository())
;
http.logout()
.deleteCookies("remove").invalidateHttpSession(true).logoutUrl("cas/logout")
.logoutSuccessUrl("/");
//http.exceptionHandling().accessDeniedPage("/403.html");
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
我的 Web.xml 上的 SSOut 过滤器,不知道我添加它的确切原因:
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>
org.jasig.cas.client.session.SingleSignOutHttpSessionListener
</listener-class>
</listener>
这是我通过 spring 安全与 cas 集成的单点注销配置:
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" />
<bean id="requestSingleLogoutFilter"
class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg
value="${cas.server.address}/logout?service=${cas.server.address}" />
<constructor-arg>
<bean
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</constructor-arg>
<property name="filterProcessesUrl" value="/j_spring_cas_security_logout" />
</bean>
并且您应该将这些过滤器添加到您的 springSecurityFilterChain
:
<sec:filter-chain pattern="/logout*"
filters="securityContextPersistenceFilter,singleLogoutFilter,casAuthenticationFilter" />
<sec:filter-chain pattern="/j_spring_cas_security_logout*"
filters="requestSingleLogoutFilter" />
我没有找到任何关于在我的应用程序上实现单点退出 CAS 功能的实际指南。我在这里尝试了很多关于 SO 的答案,但是 none 有效(比如
我的网络安全配置:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static String CAS_URL = "https://localhost:8443/cas";
private static String APP_URL = "https://localhost:8443/i9t-YM";
@Bean
public ServiceProperties serviceProperties() {
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(APP_URL+"/j_spring_cas_security_check");
serviceProperties.setSendRenew(false);
return serviceProperties;
}
@Bean
public CasAuthenticationProvider casAuthenticationProvider() {
CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
casAuthenticationProvider.setAuthenticationUserDetailsService(authenticationUserDetailsService());
casAuthenticationProvider.setServiceProperties(serviceProperties());
casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
casAuthenticationProvider.setKey("an_id_for_this_auth_provider_only");
return casAuthenticationProvider;
}
@Bean
public AuthenticationUserDetailsService authenticationUserDetailsService() {
return new TestCasAuthenticationUserDetailsService();
}
@Bean
public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
return new Cas20ServiceTicketValidator(CAS_URL);
}
@Bean
public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter.setAuthenticationManager(authenticationManager());
return casAuthenticationFilter;
}
@Bean
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
casAuthenticationEntryPoint.setLoginUrl(CAS_URL+"/login");
casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
return casAuthenticationEntryPoint;
}
@Bean
public SingleSignOutFilter SingleSignOutFilter(){
return new SingleSignOutFilter();
}
@Bean
public LogoutFilter requestLogoutFilter(){
SecurityContextLogoutHandler handler = new SecurityContextLogoutHandler();
handler.setClearAuthentication(true);
handler.setInvalidateHttpSession(true);
LogoutFilter logoutFilter = new LogoutFilter(APP_URL, handler);
return logoutFilter;
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(casAuthenticationProvider());
auth.inMemoryAuthentication().withUser("joe").password("joe").roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilter(casAuthenticationFilter());
http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint());
http.addFilterBefore(requestLogoutFilter(), LogoutFilter.class);
http.addFilterBefore(SingleSignOutFilter(), CasAuthenticationFilter.class);
http.httpBasic().and().authorizeRequests().antMatchers("/index.html", "/home.html", "/login.html", "/")
.permitAll().anyRequest().authenticated()
.and().addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.csrf().csrfTokenRepository(csrfTokenRepository())
;
http.logout()
.deleteCookies("remove").invalidateHttpSession(true).logoutUrl("cas/logout")
.logoutSuccessUrl("/");
//http.exceptionHandling().accessDeniedPage("/403.html");
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
我的 Web.xml 上的 SSOut 过滤器,不知道我添加它的确切原因:
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>
org.jasig.cas.client.session.SingleSignOutHttpSessionListener
</listener-class>
</listener>
这是我通过 spring 安全与 cas 集成的单点注销配置:
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" />
<bean id="requestSingleLogoutFilter"
class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg
value="${cas.server.address}/logout?service=${cas.server.address}" />
<constructor-arg>
<bean
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</constructor-arg>
<property name="filterProcessesUrl" value="/j_spring_cas_security_logout" />
</bean>
并且您应该将这些过滤器添加到您的 springSecurityFilterChain
:
<sec:filter-chain pattern="/logout*"
filters="securityContextPersistenceFilter,singleLogoutFilter,casAuthenticationFilter" />
<sec:filter-chain pattern="/j_spring_cas_security_logout*"
filters="requestSingleLogoutFilter" />