I am using CAS Server version 4.0.7 in a multidomain environment.
First the user logs in a portal, a PHP page (using PHP-CAS) in
tardis.com.br domain. The CAS Server is in
tardis.com.br domain too.
If the user logs in successfully, the portal enables a button that redirects to the principal CAS Service deployed in another domain and context:
https://gallifrey.com.br/principal.
The problem is that principal CAS Service invoke ajax requests to another protected CAS Service, Analytics Service and CAS returns the login page instead identify that user is logged.
The Principal Service and Analytics Service are using Spring Framework 3.2.3.RELEASE, Spring Security 3.2.9.RELEASE and CAS Client 3.4.1. The applications are using org.jasig.cas.client.validation.Cas20ProxyTicketValidator because we need to execute server side requests between services and client side requests via ajax too.
CAS Proxy Authentication in the Server Side using Spring Security CAS and CAS Client works as expected.
final CasAuthenticationToken casAuthenticationToken = (CasAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
final String proxyTicket = casAuthenticationToken.getAssertion().getPrincipal().getProxyTicketFor(targetUrl);
// The proxyTicket is generated!!!
$.ajax({
"url": "/analytics/foo/bar/xpto/",
"contentType": "application/x-www-form-urlencoded",
"data": {
"name" : "Foo"
},
"timeout": 3000,
"type": "POST",
"success": function(data, textStatus, jqXHR) {
// Returns the login page
},
"error": function(jqXHR, textStatus, errorThrown) {
// Do something
}
});
The Principal Service and Analytics Service are behind a Nginx, using reverse proxy. The Principal Service is in a WebLogic and Analytics Service in a Tomcat. And Nginx is using a SSL certificate.
Here is an example of CAS + Spring Security configuration for the Java applications:
<?xml version="1.0" encoding="UTF-8"?>
<sec:http auto-config="true" use-expressions="true" entry-point-ref="casEntryPoint">
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER" />
<sec:custom-filter ref="singleLogoutFilter" before="CAS_FILTER" />
<sec:custom-filter ref="casFilter" position="CAS_FILTER" />
<sec:logout logout-url="/logout"
logout-success-url="${cas.baseUrl}/logout?service=${service.baseUrl}/"
invalidate-session="true"
delete-cookies="JSESSIONID" />
<sec:headers>
<sec:cache-control />
</sec:headers>
</sec:http>
<sec:authentication-manager alias="authManager">
<sec:authentication-provider ref="casProxyAuthProvider" />
</sec:authentication-manager>
<bean id="userService" class="org.springframework.security.cas.userdetails.GrantedAuthorityFromAssertionAttributesUserDetailsService">
<constructor-arg>
<array>
<value>role</value>
</array>
</constructor-arg>
</bean>
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" />
<bean id="requestSingleLogoutFilter"
class="org.springframework.security.web.authentication.logout.LogoutFilter"
p:filterProcessesUrl="/j_spring_cas_security_logout">
<constructor-arg value="${cas.baseUrl}/logout" />
<constructor-arg>
<bean
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</constructor-arg>
</bean>
<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties"
p:service="${service.baseUrl}/j_spring_cas_security_check"
p:sendRenew="false" p:authenticateAllArtifacts="true" />
<bean id="casEntryPoint"
class="org.springframework.security.cas.web.CasAuthenticationEntryPoint"
p:serviceProperties-ref="serviceProperties" p:loginUrl="${cas.baseUrl}/login" />
<bean id="casFilter"
class="org.springframework.security.cas.web.CasAuthenticationFilter"
p:authenticationManager-ref="authManager" p:serviceProperties-ref="serviceProperties"
p:proxyGrantingTicketStorage-ref="pgtStorage"
p:proxyReceptorUrl="/j_spring_cas_security_proxyreceptor">
<property name="authenticationDetailsSource">
<bean
class="org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource">
<constructor-arg ref="serviceProperties" />
</bean>
</property>
<property name="authenticationFailureHandler">
<bean
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
</bean>
</property>
</bean>
<bean id="pgtStorage"
class="org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl" />
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>WEB-INF/classes/ehcache.xml</value>
</property>
</bean>
<bean id="casTicketsCache" class="net.sf.ehcache.Cache"
factory-bean="cacheManager" factory-method="getCache">
<constructor-arg value="casWebServiceTickets" />
</bean>
<bean id="casProxyAuthProvider"
class="org.springframework.security.cas.authentication.CasAuthenticationProvider"
p:serviceProperties-ref="serviceProperties" p:key="casProxyAuthProviderKey"
p:authenticationUserDetailsService-ref="userService">
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator"
p:proxyCallbackUrl="${service.baseUrl}/j_spring_cas_security_proxyreceptor"
p:proxyGrantingTicketStorage-ref="pgtStorage" p:acceptAnyProxy="true">
<constructor-arg value="${cas.baseUrl}" />
</bean>
</property>
<property name="statelessTicketCache">
<bean
class="org.springframework.security.cas.authentication.EhCacheBasedTicketCache">
<property name="cache" ref="casTicketsCache" />
</bean>
</property>
</bean>
</beans>
Maven dependencies:
<properties>
<java.version>1.7</java.version>
<spring.version>3.2.3.RELEASE</spring.version>
<spring-security.version>3.2.9.RELEASE</spring-security.version>
<cas-client.version>3.4.1</cas-client.version>
<ehcache.version>2.9.0</ehcache.version>
</properties>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>${cas-client.version}</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>${ehcache.version}</version>
</dependency>
CAS Server Service List:
deployerConfigContext.xml:
<util:list id="registeredServicesList">
<bean class="org.jasig.cas.services.RegexRegisteredService" p:id="0"
p:name="HTTP and IMAP" p:description="Allows HTTP(S) and IMAP(S) protocols"
p:serviceId="^(https?|imaps?)://.*" p:evaluationOrder="10000001"
p:enabled="true" p:allowedToProxy="true" p:ssoEnabled="true" />
</util:list>
Tomcat configuration (server.xml):
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
maxThreads="1000"
URIEncoding="UTF-8"
redirectPort="8443" />
In a single domain environment everything works as expected but in a multidomain environment CAS Server redirect to login page in first XHR request instead detect that user is logged. Any ideas why this occurs?