Hi Rob, finally I've got it working.
Unfortunately your 965 branch solution ended as all others with 302 status code response.
So I've decided to write custom filter based on your solution. This is it's raw version (changed/added methods)
Code:
package security.test2.cas.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
import org.jasig.cas.client.validation.TicketValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import security.test2.sso.cas.CASAuthentication;
import security.test2.sso.cas.CASPrincipalWrapper;
public class CasFilter extends AbstractAuthenticationProcessingFilter {
private static final Logger logger = LoggerFactory.getLogger(CasFilter.class);
//~ Static fields/initializers =====================================================================================
/** Used to identify a CAS request for a stateful user agent, such as a web browser. */
public static final String CAS_STATEFUL_IDENTIFIER = "_cas_stateful_";
/**
* Used to identify a CAS request for a stateless user agent, such as a remoting protocol client (e.g.
* Hessian, Burlap, SOAP etc). Results in a more aggressive caching strategy being used, as the absence of a
* <code>HttpSession</code> will result in a new authentication attempt on every request.
*/
public static final String CAS_STATELESS_IDENTIFIER = "_cas_stateless_";
/**
* The last portion of the receptor url, i.e. /proxy/receptor
*/
private String proxyReceptorUrl;
/**
* The backing storage to store ProxyGrantingTicket requests.
*/
private ProxyGrantingTicketStorage proxyGrantingTicketStorage;
private String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER;
private Cas20ProxyTicketValidator ticketValidator;
private String serverName;
private UserDetailsService userDetailsService;
//~ Constructors ===================================================================================================
public CasFilter() {
super("/j_spring_cas_security_check");
}
//~ Methods ========================================================================================================
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult)
throws IOException, ServletException {
// boolean continueFilterChain = proxyTicketRequest(serviceTicketRequest(request, response), request);
// if (!continueFilterChain) {
// super.successfulAuthentication(request, response, authResult);
// return;
// }
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
}
/* (non-Javadoc)
* @see org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// refers to setting ticket at CasAuthenticationCommonsHttpInvokerRequestExecutor
// if ticket is set by postMethod.addParameter it's not possible to get it by request.getParameter
String proxyTicket = request.getHeader(this.artifactParameter) != null
? new String(Base64.decode(request.getHeader(this.artifactParameter).getBytes())) : null;
logger.info("doFilter - URI: " + request.getRequestURI());
logger.info("doFilter - ticket: " + proxyTicket);
if (proxyTicket == null || "".equals(proxyTicket)) {
chain.doFilter(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Assertion assertion = null;
Authentication authResult = null;
try {
assertion = this.ticketValidator.validate(proxyTicket,
CommonUtils.constructServiceUrl(request, response, null, this.serverName, this.artifactParameter, true));
if (assertion != null) {
AttributePrincipal attributePrincipal = assertion.getPrincipal();
logger.info(attributePrincipal.getName());
} else {
return;
}
CASAuthentication authRequest = new CASAuthentication();
authRequest.setAssertion(assertion);
authRequest.setAuthenticated(true);
authRequest.setTicket(proxyTicket);
authRequest.setPrincipalWrapper(new CASPrincipalWrapper(assertion.getPrincipal().getName()));
UserDetails userDetails = this.userDetailsService.loadUserByUsername(authRequest.getName());
authRequest.getAuthorities().addAll(userDetails.getAuthorities());
// TODO move code to some authentication provider
authResult = authRequest;
// authResult = this.getAuthenticationManager().authenticate(authRequest);
// authResult = attemptAuthentication(request, response);
// if (authResult == null) {
// // return immediately as subclass has indicated that it hasn't completed authentication
// return;
// }
// sessionStrategy.onAuthentication(authResult, request, response);
} catch (TicketValidationException failed) {
// TODO
return;
} catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed);
return;
}
// Authentication success
// if (continueChainBeforeSuccessfulAuthentication) {
// chain.doFilter(request, response);
// }
successfulAuthentication(request, response, authResult);
chain.doFilter(request, response);
}
...
public final void setArtifactParameter(final String artifactParameter) {
this.artifactParameter = artifactParameter;
}
public final void setTicketValidator(final Cas20ProxyTicketValidator ticketValidator) {
this.ticketValidator = ticketValidator;
}
public final void setServerName(final String serverName) {
this.serverName = serverName;
}
public final void setUserDetailsService(final UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
...
}