mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-31 09:12:14 +00:00
SEC-965: Added support for CAS proxy ticket authentication on any URL
This commit is contained in:
parent
373d07ce46
commit
a76a947b12
@ -38,6 +38,8 @@ public class ServiceProperties implements InitializingBean {
|
|||||||
|
|
||||||
private String service;
|
private String service;
|
||||||
|
|
||||||
|
private boolean authenticateAllArtifacts;
|
||||||
|
|
||||||
private boolean sendRenew = false;
|
private boolean sendRenew = false;
|
||||||
|
|
||||||
private String artifactParameter = DEFAULT_CAS_ARTIFACT_PARAMETER;
|
private String artifactParameter = DEFAULT_CAS_ARTIFACT_PARAMETER;
|
||||||
@ -47,7 +49,9 @@ public class ServiceProperties implements InitializingBean {
|
|||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
Assert.hasLength(this.service, "service must be specified.");
|
if(!authenticateAllArtifacts) {
|
||||||
|
Assert.hasLength(this.service, "service must be specified unless authenticateAllArtifacts is true.");
|
||||||
|
}
|
||||||
Assert.hasLength(this.artifactParameter, "artifactParameter cannot be empty.");
|
Assert.hasLength(this.artifactParameter, "artifactParameter cannot be empty.");
|
||||||
Assert.hasLength(this.serviceParameter, "serviceParameter cannot be empty.");
|
Assert.hasLength(this.serviceParameter, "serviceParameter cannot be empty.");
|
||||||
}
|
}
|
||||||
@ -115,4 +119,19 @@ public class ServiceProperties implements InitializingBean {
|
|||||||
public final void setServiceParameter(final String serviceParameter) {
|
public final void setServiceParameter(final String serviceParameter) {
|
||||||
this.serviceParameter = serviceParameter;
|
this.serviceParameter = serviceParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final boolean isAuthenticateAllArtifacts() {
|
||||||
|
return authenticateAllArtifacts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, then any non-null artifact (ticket) should be authenticated.
|
||||||
|
* Additionally, the service will be determined dynamically in order to
|
||||||
|
* ensure the service matches the expected value for this artifact.
|
||||||
|
*
|
||||||
|
* @param authenticateAllArtifacts
|
||||||
|
*/
|
||||||
|
public final void setAuthenticateAllArtifacts(final boolean authenticateAllArtifacts) {
|
||||||
|
this.authenticateAllArtifacts = authenticateAllArtifacts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.cas.authentication;
|
package org.springframework.security.cas.authentication;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.jasig.cas.client.validation.Assertion;
|
import org.jasig.cas.client.validation.Assertion;
|
||||||
import org.jasig.cas.client.validation.TicketValidationException;
|
import org.jasig.cas.client.validation.TicketValidationException;
|
||||||
import org.jasig.cas.client.validation.TicketValidator;
|
import org.jasig.cas.client.validation.TicketValidator;
|
||||||
@ -28,6 +30,7 @@ import org.springframework.security.authentication.BadCredentialsException;
|
|||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.cas.ServiceProperties;
|
import org.springframework.security.cas.ServiceProperties;
|
||||||
import org.springframework.security.cas.web.CasAuthenticationFilter;
|
import org.springframework.security.cas.web.CasAuthenticationFilter;
|
||||||
|
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||||
@ -50,6 +53,9 @@ import org.springframework.util.Assert;
|
|||||||
* @author Scott Battaglia
|
* @author Scott Battaglia
|
||||||
*/
|
*/
|
||||||
public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
|
public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
|
||||||
|
//~ Static fields/initializers =====================================================================================
|
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
|
||||||
|
|
||||||
//~ Instance fields ================================================================================================
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
@ -72,7 +78,6 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
|||||||
Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set");
|
Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set");
|
||||||
Assert.hasText(this.key, "A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
|
Assert.hasText(this.key, "A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
|
||||||
Assert.notNull(this.messages, "A message source must be set");
|
Assert.notNull(this.messages, "A message source must be set");
|
||||||
Assert.notNull(this.serviceProperties, "serviceProperties is a required field.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
@ -132,7 +137,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
|||||||
|
|
||||||
private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
|
private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
|
||||||
try {
|
try {
|
||||||
final Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(), serviceProperties.getService());
|
final Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(), getServiceUrl(authentication));
|
||||||
final UserDetails userDetails = loadUserByAssertion(assertion);
|
final UserDetails userDetails = loadUserByAssertion(assertion);
|
||||||
userDetailsChecker.check(userDetails);
|
userDetailsChecker.check(userDetails);
|
||||||
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(),
|
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(),
|
||||||
@ -142,6 +147,32 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the serviceUrl. If the {@link Authentication#getDetails()} is an
|
||||||
|
* instance of {@link ServiceAuthenticationDetails}, then
|
||||||
|
* {@link ServiceAuthenticationDetails#getServiceUrl()} is used. Otherwise,
|
||||||
|
* the {@link ServiceProperties#getService()} is used.
|
||||||
|
*
|
||||||
|
* @param authentication
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String getServiceUrl(Authentication authentication) {
|
||||||
|
String serviceUrl;
|
||||||
|
if(authentication.getDetails() instanceof ServiceAuthenticationDetails) {
|
||||||
|
serviceUrl = ((ServiceAuthenticationDetails)authentication.getDetails()).getServiceUrl();
|
||||||
|
}else if(serviceProperties == null){
|
||||||
|
throw new IllegalStateException("serviceProperties cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");
|
||||||
|
}else if(serviceProperties.getService() == null){
|
||||||
|
throw new IllegalStateException("serviceProperties.getService() cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");
|
||||||
|
}else {
|
||||||
|
serviceUrl = serviceProperties.getService();
|
||||||
|
}
|
||||||
|
if(logger.isDebugEnabled()) {
|
||||||
|
logger.debug("serviceUrl = "+serviceUrl);
|
||||||
|
}
|
||||||
|
return serviceUrl;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Template method for retrieving the UserDetails based on the assertion. Default is to call configured userDetailsService and pass the username. Deployers
|
* Template method for retrieving the UserDetails based on the assertion. Default is to call configured userDetailsService and pass the username. Deployers
|
||||||
* can override this method and retrieve the user based on any criteria they desire.
|
* can override this method and retrieve the user based on any criteria they desire.
|
||||||
|
@ -65,6 +65,7 @@ public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, In
|
|||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
Assert.hasLength(this.loginUrl, "loginUrl must be specified");
|
Assert.hasLength(this.loginUrl, "loginUrl must be specified");
|
||||||
Assert.notNull(this.serviceProperties, "serviceProperties must be specified");
|
Assert.notNull(this.serviceProperties, "serviceProperties must be specified");
|
||||||
|
Assert.notNull(this.serviceProperties.getService(),"serviceProperties.getService() cannot be null.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void commence(final HttpServletRequest servletRequest, final HttpServletResponse response,
|
public final void commence(final HttpServletRequest servletRequest, final HttpServletResponse response,
|
||||||
|
@ -17,41 +17,139 @@ package org.springframework.security.cas.web;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||||
import org.jasig.cas.client.util.CommonUtils;
|
import org.jasig.cas.client.util.CommonUtils;
|
||||||
import org.jasig.cas.client.validation.TicketValidator;
|
import org.jasig.cas.client.validation.TicketValidator;
|
||||||
|
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||||
|
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
|
||||||
import org.springframework.security.cas.ServiceProperties;
|
import org.springframework.security.cas.ServiceProperties;
|
||||||
|
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
|
||||||
|
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||||
|
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a CAS service ticket.
|
* Processes a CAS service ticket, obtains proxy granting tickets, and processes proxy tickets.
|
||||||
|
* <h2>Service Tickets</h2>
|
||||||
* <p>
|
* <p>
|
||||||
* A service ticket consists of an opaque ticket string. It arrives at this filter by the user's browser successfully
|
* A service ticket consists of an opaque ticket string. It arrives at this filter by the user's browser successfully
|
||||||
* authenticating using CAS, and then receiving a HTTP redirect to a <code>service</code>. The opaque ticket string is
|
* authenticating using CAS, and then receiving a HTTP redirect to a <code>service</code>. The opaque ticket string is
|
||||||
* presented in the <code>ticket</code> request parameter. This filter monitors the <code>service</code> URL so it can
|
* presented in the <code>ticket</code> request parameter.
|
||||||
* receive the service ticket and process it. The CAS server knows which <code>service</code> URL to use via the
|
* <p>
|
||||||
* {@link ServiceProperties#getService()} method.
|
* This filter monitors the <code>service</code> URL so it can
|
||||||
|
* receive the service ticket and process it. By default this filter processes the URL <tt>/j_spring_cas_security_check</tt>.
|
||||||
|
* When processing this URL, the value of {@link ServiceProperties#getService()} is used as the <tt>service</tt> when validating
|
||||||
|
* the <code>ticket</code>. This means that it is important that {@link ServiceProperties#getService()} specifies the same value
|
||||||
|
* as the <tt>filterProcessesUrl</tt>.
|
||||||
* <p>
|
* <p>
|
||||||
* Processing the service ticket involves creating a <code>UsernamePasswordAuthenticationToken</code> which
|
* Processing the service ticket involves creating a <code>UsernamePasswordAuthenticationToken</code> which
|
||||||
* uses {@link #CAS_STATEFUL_IDENTIFIER} for the <code>principal</code> and the opaque ticket string as the
|
* uses {@link #CAS_STATEFUL_IDENTIFIER} for the <code>principal</code> and the opaque ticket string as the
|
||||||
* <code>credentials</code>.
|
* <code>credentials</code>.
|
||||||
|
* <h2>Obtaining Proxy Granting Tickets</h2>
|
||||||
|
* <p>
|
||||||
|
* If specified, the filter can also monitor the <code>proxyReceptorUrl</code>. The filter will respond to requests matching
|
||||||
|
* this url so that the CAS Server can provide a PGT to the filter. Note that in addition to the <code>proxyReceptorUrl</code> a non-null
|
||||||
|
* <code>proxyGrantingTicketStorage</code> must be provided in order for the filter to respond to proxy receptor requests. By configuring
|
||||||
|
* a shared {@link ProxyGrantingTicketStorage} between the {@link TicketValidator} and the CasAuthenticationFilter one can have the
|
||||||
|
* CasAuthenticationFilter handle the proxying requirements for CAS.
|
||||||
|
* <h2>Proxy Tickets</h2>
|
||||||
|
* <p>
|
||||||
|
* The filter can process tickets present on any url. This is useful when wanting to process proxy tickets. In order for proxy
|
||||||
|
* tickets to get processed {@link ServiceProperties#isAuthenticateAllArtifacts()} must return <code>true</code>. Additionally,
|
||||||
|
* if the request is already authenticated, authentication will <b>not</b> occur. Last, {@link AuthenticationDetailsSource#buildDetails(Object)}
|
||||||
|
* must return a {@link ServiceAuthenticationDetails}. This can be accomplished using the {@link ServiceAuthenticationDetailsSource}.
|
||||||
|
* In this case {@link ServiceAuthenticationDetails#getServiceUrl()} will be used for the service url.
|
||||||
|
* <p>
|
||||||
|
* Processing the proxy ticket involves creating a <code>UsernamePasswordAuthenticationToken</code> which
|
||||||
|
* uses {@link #CAS_STATELESS_IDENTIFIER} for the <code>principal</code> and the opaque ticket string as the
|
||||||
|
* <code>credentials</code>. When a proxy ticket is successfully authenticated, the FilterChain continues and the
|
||||||
|
* <code>authenticationSuccessHandler</code> is not used.
|
||||||
|
* <h2>Notes about the <code>AuthenticationManager</code></h2>
|
||||||
* <p>
|
* <p>
|
||||||
* The configured <code>AuthenticationManager</code> is expected to provide a provider that can recognise
|
* The configured <code>AuthenticationManager</code> is expected to provide a provider that can recognise
|
||||||
* <code>UsernamePasswordAuthenticationToken</code>s containing this special <code>principal</code> name, and process
|
* <code>UsernamePasswordAuthenticationToken</code>s containing this special <code>principal</code> name, and process
|
||||||
* them accordingly by validation with the CAS server.
|
* them accordingly by validation with the CAS server. Additionally, it should be capable of using the result of
|
||||||
|
* {@link ServiceAuthenticationDetails#getServiceUrl()} as the service when validating the ticket.
|
||||||
|
* <h2>Example Configuration</h2>
|
||||||
* <p>
|
* <p>
|
||||||
* By configuring a shared {@link ProxyGrantingTicketStorage} between the {@link TicketValidator} and the
|
* An example configuration that supports service tickets, obtaining proxy granting tickets, and proxy tickets is
|
||||||
* CasAuthenticationFilter one can have the CasAuthenticationFilter handle the proxying requirements for CAS. In addition, the
|
* illustrated below:
|
||||||
* URI endpoint for the proxying would also need to be configured (i.e. the part after protocol, hostname, and port).
|
*
|
||||||
* <p>
|
* <pre>
|
||||||
* By default this filter processes the URL <tt>/j_spring_cas_security_check</tt>.
|
* <b:bean id="serviceProperties"
|
||||||
|
* class="org.springframework.security.cas.ServiceProperties"
|
||||||
|
* p:service="https://service.example.com/cas-sample/j_spring_cas_security_check"
|
||||||
|
* p:authenticateAllArtifacts="true"/>
|
||||||
|
* <b:bean id="casEntryPoint"
|
||||||
|
* class="org.springframework.security.cas.web.CasAuthenticationEntryPoint"
|
||||||
|
* p:serviceProperties-ref="serviceProperties" p:loginUrl="https://login.example.org/cas/login" />
|
||||||
|
* <b: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">
|
||||||
|
* <b:property name="authenticationDetailsSource">
|
||||||
|
* <b:bean class="org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource"/>
|
||||||
|
* </b:property>
|
||||||
|
* <b:property name="authenticationFailureHandler">
|
||||||
|
* <b:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
|
||||||
|
* p:defaultFailureUrl="/casfailed.jsp"/>
|
||||||
|
* </b:property>
|
||||||
|
* </b:bean>
|
||||||
|
* <!--
|
||||||
|
* NOTE: In a real application you should not use an in memory implementation. You will also want
|
||||||
|
* to ensure to clean up expired tickets by calling ProxyGrantingTicketStorage.cleanup()
|
||||||
|
* -->
|
||||||
|
* <b:bean id="pgtStorage" class="org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl"/>
|
||||||
|
* <b:bean id="casAuthProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider"
|
||||||
|
* p:serviceProperties-ref="serviceProperties"
|
||||||
|
* p:key="casAuthProviderKey">
|
||||||
|
* <b:property name="authenticationUserDetailsService">
|
||||||
|
* <b:bean
|
||||||
|
* class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
|
||||||
|
* <b:constructor-arg ref="userService" />
|
||||||
|
* </b:bean>
|
||||||
|
* </b:property>
|
||||||
|
* <b:property name="ticketValidator">
|
||||||
|
* <b:bean
|
||||||
|
* class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator"
|
||||||
|
* p:acceptAnyProxy="true"
|
||||||
|
* p:proxyCallbackUrl="https://service.example.com/cas-sample/j_spring_cas_security_proxyreceptor"
|
||||||
|
* p:proxyGrantingTicketStorage-ref="pgtStorage">
|
||||||
|
* <b:constructor-arg value="https://login.example.org/cas" />
|
||||||
|
* </b:bean>
|
||||||
|
* </b:property>
|
||||||
|
* <b:property name="statelessTicketCache">
|
||||||
|
* <b:bean class="org.springframework.security.cas.authentication.EhCacheBasedTicketCache">
|
||||||
|
* <b:property name="cache">
|
||||||
|
* <b:bean class="net.sf.ehcache.Cache"
|
||||||
|
* init-method="initialise"
|
||||||
|
* destroy-method="dispose">
|
||||||
|
* <b:constructor-arg value="casTickets"/>
|
||||||
|
* <b:constructor-arg value="50"/>
|
||||||
|
* <b:constructor-arg value="true"/>
|
||||||
|
* <b:constructor-arg value="false"/>
|
||||||
|
* <b:constructor-arg value="3600"/>
|
||||||
|
* <b:constructor-arg value="900"/>
|
||||||
|
* </b:bean>
|
||||||
|
* </b:property>
|
||||||
|
* </b:bean>
|
||||||
|
* </b:property>
|
||||||
|
* </b:bean>
|
||||||
|
* </pre>
|
||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
@ -81,26 +179,59 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
|||||||
|
|
||||||
private String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER;
|
private String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER;
|
||||||
|
|
||||||
|
private boolean authenticateAllArtifacts;
|
||||||
|
|
||||||
|
private AuthenticationFailureHandler proxyFailureHandler = new SimpleUrlAuthenticationFailureHandler();
|
||||||
|
|
||||||
//~ Constructors ===================================================================================================
|
//~ Constructors ===================================================================================================
|
||||||
|
|
||||||
public CasAuthenticationFilter() {
|
public CasAuthenticationFilter() {
|
||||||
super("/j_spring_cas_security_check");
|
super("/j_spring_cas_security_check");
|
||||||
|
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final void successfulAuthentication(HttpServletRequest request,
|
||||||
|
HttpServletResponse response, FilterChain chain, Authentication authResult)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
boolean continueFilterChain = proxyTicketRequest(serviceTicketRequest(request, response),request);
|
||||||
|
if(!continueFilterChain) {
|
||||||
|
super.successfulAuthentication(request, response, chain, 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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response)
|
public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response)
|
||||||
throws AuthenticationException, IOException {
|
throws AuthenticationException, IOException {
|
||||||
// if the request is a proxy request process it and return null to indicate the request has been processed
|
// if the request is a proxy request process it and return null to indicate the request has been processed
|
||||||
if(isProxyRequest(request)) {
|
if(proxyReceptorRequest(request)) {
|
||||||
|
logger.debug("Responding to proxy receptor request");
|
||||||
CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);
|
CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String username = CAS_STATEFUL_IDENTIFIER;
|
final boolean serviceTicketRequest = serviceTicketRequest(request, response);
|
||||||
String password = request.getParameter(this.artifactParameter);
|
final String username = serviceTicketRequest ? CAS_STATEFUL_IDENTIFIER : CAS_STATELESS_IDENTIFIER;
|
||||||
|
String password = obtainArtifact(request);
|
||||||
|
|
||||||
if (password == null) {
|
if (password == null) {
|
||||||
|
logger.debug("Failed to obtain an artifact (cas ticket)");
|
||||||
password = "";
|
password = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,11 +242,47 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
|||||||
return this.getAuthenticationManager().authenticate(authRequest);
|
return this.getAuthenticationManager().authenticate(authRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If present, gets the artifact (CAS ticket) from the {@link HttpServletRequest}.
|
||||||
|
* @param request
|
||||||
|
* @return if present the artifact from the {@link HttpServletRequest}, else null
|
||||||
|
*/
|
||||||
|
protected String obtainArtifact(HttpServletRequest request) {
|
||||||
|
return request.getParameter(artifactParameter);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overridden to provide proxying capabilities.
|
* Overridden to provide proxying capabilities.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
protected boolean requiresAuthentication(final HttpServletRequest request, final HttpServletResponse response) {
|
protected boolean requiresAuthentication(final HttpServletRequest request, final HttpServletResponse response) {
|
||||||
return isProxyRequest(request) || super.requiresAuthentication(request, response);
|
final boolean serviceTicketRequest = serviceTicketRequest(request, response);
|
||||||
|
final boolean result = serviceTicketRequest || proxyReceptorRequest(request) || (proxyTicketRequest(serviceTicketRequest, request));
|
||||||
|
if(logger.isDebugEnabled()) {
|
||||||
|
logger.debug("requiresAuthentication = "+result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link AuthenticationFailureHandler} for proxy requests.
|
||||||
|
* @param proxyFailureHandler
|
||||||
|
*/
|
||||||
|
public final void setProxyAuthenticationFailureHandler(
|
||||||
|
AuthenticationFailureHandler proxyFailureHandler) {
|
||||||
|
Assert.notNull(proxyFailureHandler,"proxyFailureHandler cannot be null");
|
||||||
|
this.proxyFailureHandler = proxyFailureHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps the {@link AuthenticationFailureHandler} to distinguish between
|
||||||
|
* handling proxy ticket authentication failures and service ticket
|
||||||
|
* failures.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final void setAuthenticationFailureHandler(
|
||||||
|
AuthenticationFailureHandler failureHandler) {
|
||||||
|
super.setAuthenticationFailureHandler(new CasAuthenticationFailureHandler(failureHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
|
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
|
||||||
@ -129,15 +296,97 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
|||||||
|
|
||||||
public final void setServiceProperties(final ServiceProperties serviceProperties) {
|
public final void setServiceProperties(final ServiceProperties serviceProperties) {
|
||||||
this.artifactParameter = serviceProperties.getArtifactParameter();
|
this.artifactParameter = serviceProperties.getArtifactParameter();
|
||||||
|
this.authenticateAllArtifacts = serviceProperties.isAuthenticateAllArtifacts();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if the request is eligible to be processed as a proxy request.
|
* Indicates if the request is elgible to process a service ticket. This method exists for readability.
|
||||||
|
* @param request
|
||||||
|
* @param response
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private boolean serviceTicketRequest(final HttpServletRequest request, final HttpServletResponse response) {
|
||||||
|
boolean result = super.requiresAuthentication(request, response);
|
||||||
|
if(logger.isDebugEnabled()) {
|
||||||
|
logger.debug("serviceTicketRequest = "+result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the request is elgible to process a proxy ticket.
|
||||||
* @param request
|
* @param request
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private boolean isProxyRequest(final HttpServletRequest request) {
|
private boolean proxyTicketRequest(final boolean serviceTicketRequest, final HttpServletRequest request) {
|
||||||
final String requestUri = request.getRequestURI();
|
if(serviceTicketRequest) {
|
||||||
return this.proxyGrantingTicketStorage != null && !CommonUtils.isEmpty(this.proxyReceptorUrl) && requestUri.endsWith(this.proxyReceptorUrl);
|
return false;
|
||||||
|
}
|
||||||
|
final boolean result = authenticateAllArtifacts && obtainArtifact(request) != null && !authenticated();
|
||||||
|
if(logger.isDebugEnabled()) {
|
||||||
|
logger.debug("proxyTicketRequest = "+result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Determines if a user is already authenticated.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private boolean authenticated() {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
return authentication != null && authentication.isAuthenticated() && !(authentication instanceof AnonymousAuthenticationToken);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Indicates if the request is elgible to be processed as the proxy receptor.
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private boolean proxyReceptorRequest(final HttpServletRequest request) {
|
||||||
|
final String requestUri = request.getRequestURI();
|
||||||
|
final boolean result = proxyReceptorConfigured() && requestUri.endsWith(this.proxyReceptorUrl);
|
||||||
|
if(logger.isDebugEnabled()) {
|
||||||
|
logger.debug("proxyReceptorRequest = "+result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the {@link CasAuthenticationFilter} is configured to handle the proxy receptor requests.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private boolean proxyReceptorConfigured() {
|
||||||
|
final boolean result = this.proxyGrantingTicketStorage != null && !CommonUtils.isEmpty(this.proxyReceptorUrl);
|
||||||
|
if(logger.isDebugEnabled()) {
|
||||||
|
logger.debug("proxyReceptorConfigured = "+result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper for the AuthenticationFailureHandler that will flex the {@link AuthenticationFailureHandler} that is used. The value
|
||||||
|
* {@link CasAuthenticationFilter#setProxyAuthenticationFailureHandler(AuthenticationFailureHandler) will be used for proxy requests
|
||||||
|
* that fail. The value {@link CasAuthenticationFilter#setAuthenticationFailureHandler(AuthenticationFailureHandler)} will be used for
|
||||||
|
* service tickets that fail.
|
||||||
|
*
|
||||||
|
* @author Rob Winch
|
||||||
|
*/
|
||||||
|
private class CasAuthenticationFailureHandler implements AuthenticationFailureHandler {
|
||||||
|
private final AuthenticationFailureHandler serviceTicketFailureHandler;
|
||||||
|
public CasAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
|
||||||
|
Assert.notNull(failureHandler,"failureHandler");
|
||||||
|
this.serviceTicketFailureHandler = failureHandler;
|
||||||
|
}
|
||||||
|
public void onAuthenticationFailure(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
AuthenticationException exception) throws IOException,
|
||||||
|
ServletException {
|
||||||
|
if(serviceTicketRequest(request, response)) {
|
||||||
|
serviceTicketFailureHandler.onAuthenticationFailure(request, response, exception);
|
||||||
|
}else {
|
||||||
|
proxyFailureHandler.onAuthenticationFailure(request, response, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.cas.web.authentication;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||||
|
import org.springframework.security.web.util.UrlUtils;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A default implementation of {@link ServiceAuthenticationDetails} that figures
|
||||||
|
* out the value for {@link #getServiceUrl()} by inspecting the current
|
||||||
|
* {@link HttpServletRequest} and using the current URL minus the artifact and
|
||||||
|
* the corresponding value.
|
||||||
|
*
|
||||||
|
* @author Rob Winch
|
||||||
|
*/
|
||||||
|
final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails implements ServiceAuthenticationDetails {
|
||||||
|
private static final long serialVersionUID = 6192409090610517700L;
|
||||||
|
|
||||||
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
|
private final String serviceUrl;
|
||||||
|
|
||||||
|
//~ Constructors ===================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
* @param request
|
||||||
|
* the current {@link HttpServletRequest} to obtain the
|
||||||
|
* {@link #getServiceUrl()} from.
|
||||||
|
* @param artifactPattern
|
||||||
|
* the {@link Pattern} that will be used to clean up the query
|
||||||
|
* string from containing the artifact name and value. This can
|
||||||
|
* be created using {@link #createArtifactPattern(String)}.
|
||||||
|
*/
|
||||||
|
DefaultServiceAuthenticationDetails(HttpServletRequest request, Pattern artifactPattern) {
|
||||||
|
super(request);
|
||||||
|
final String query = getQueryString(request,artifactPattern);
|
||||||
|
this.serviceUrl = UrlUtils.buildFullRequestUrl(request.getScheme(),
|
||||||
|
request.getServerName(), request.getServerPort(),
|
||||||
|
request.getRequestURI(), query);
|
||||||
|
}
|
||||||
|
|
||||||
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current URL minus the artifact parameter and its value, if present.
|
||||||
|
* @see org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails#getServiceUrl()
|
||||||
|
*/
|
||||||
|
public String getServiceUrl() {
|
||||||
|
return serviceUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = super.hashCode();
|
||||||
|
result = prime * result
|
||||||
|
+ serviceUrl.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!super.equals(obj) || !(obj instanceof DefaultServiceAuthenticationDetails)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ServiceAuthenticationDetails that = (ServiceAuthenticationDetails) obj;
|
||||||
|
return serviceUrl.equals(that.getServiceUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
result.append(super.toString());
|
||||||
|
result.append("ServiceUrl: ");
|
||||||
|
result.append(serviceUrl);
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If present, removes the artifactParameterName and the corresponding value from the query String.
|
||||||
|
* @param request
|
||||||
|
* @return the query String minus the artifactParameterName and the corresponding value.
|
||||||
|
*/
|
||||||
|
private String getQueryString(final HttpServletRequest request, final Pattern artifactPattern) {
|
||||||
|
final String query = request.getQueryString();
|
||||||
|
if(query == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final String result = artifactPattern.matcher(query).replaceFirst("");
|
||||||
|
if(result.length() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// strip off the trailing & only if the artifact was the first query param
|
||||||
|
return result.startsWith("&") ? result.substring(1) : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Pattern} that can be passed into the constructor. This
|
||||||
|
* allows the {@link Pattern} to be reused for every instance of
|
||||||
|
* {@link DefaultServiceAuthenticationDetails}.
|
||||||
|
*
|
||||||
|
* @param artifactParameterName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
static Pattern createArtifactPattern(String artifactParameterName) {
|
||||||
|
Assert.hasLength(artifactParameterName);
|
||||||
|
return Pattern.compile("&?"+Pattern.quote(artifactParameterName)+"=[^&]*");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.cas.web.authentication;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import org.springframework.security.cas.ServiceProperties;
|
||||||
|
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In order for the {@link CasAuthenticationProvider} to provide the correct
|
||||||
|
* service url to authenticate the ticket, the returned value of
|
||||||
|
* {@link Authentication#getDetails()} should implement this interface when
|
||||||
|
* tickets can be sent to any URL rather than only
|
||||||
|
* {@link ServiceProperties#getService()}.
|
||||||
|
*
|
||||||
|
* @author Rob Winch
|
||||||
|
*
|
||||||
|
* @see ServiceAuthenticationDetailsSource
|
||||||
|
*/
|
||||||
|
public interface ServiceAuthenticationDetails extends Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the absolute service url (i.e. https://example.com/service/).
|
||||||
|
*
|
||||||
|
* @return the service url. Cannot be <code>null</code>.
|
||||||
|
*/
|
||||||
|
String getServiceUrl();
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.cas.web.authentication;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||||
|
import org.springframework.security.cas.ServiceProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code AuthenticationDetailsSource} that is set on the
|
||||||
|
* {@code CasAuthenticationFilter} should return a value that implements
|
||||||
|
* {@code ServiceAuthenticationDetails} if the application needs to authenticate
|
||||||
|
* dynamic service urls. The
|
||||||
|
* {@code ServiceAuthenticationDetailsSource#buildDetails(HttpServletRequest)}
|
||||||
|
* creates a default {@code ServiceAuthenticationDetails}.
|
||||||
|
*
|
||||||
|
* @author Rob Winch
|
||||||
|
*/
|
||||||
|
public class ServiceAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest,
|
||||||
|
ServiceAuthenticationDetails> {
|
||||||
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
|
private final Pattern artifactPattern;
|
||||||
|
|
||||||
|
//~ Constructors ===================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an implementation that uses the default CAS artifactParameterName.
|
||||||
|
*/
|
||||||
|
public ServiceAuthenticationDetailsSource() {
|
||||||
|
this(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an implementation that uses the specified artifactParameterName
|
||||||
|
*
|
||||||
|
* @param artifactParameterName
|
||||||
|
* the artifactParameterName that is removed from the current
|
||||||
|
* URL. The result becomes the service url. Cannot be null and
|
||||||
|
* cannot be an empty String.
|
||||||
|
*/
|
||||||
|
public ServiceAuthenticationDetailsSource(final String artifactParameterName) {
|
||||||
|
this.artifactPattern = DefaultServiceAuthenticationDetails.createArtifactPattern(artifactParameterName);
|
||||||
|
}
|
||||||
|
|
||||||
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param context the {@code HttpServletRequest} object.
|
||||||
|
* @return the {@code ServiceAuthenticationDetails} containing information about the current request
|
||||||
|
*/
|
||||||
|
public ServiceAuthenticationDetails buildDetails(HttpServletRequest context) {
|
||||||
|
return new DefaultServiceAuthenticationDetails(context,artifactPattern);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
* Authentication processing mechanisms which respond to the submission of authentication
|
||||||
|
* credentials using CAS.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.cas.web.authentication;
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
package org.springframework.security.cas.authentication;
|
package org.springframework.security.cas.authentication;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import org.jasig.cas.client.validation.Assertion;
|
import org.jasig.cas.client.validation.Assertion;
|
||||||
@ -23,11 +23,13 @@ import org.jasig.cas.client.validation.AssertionImpl;
|
|||||||
import org.jasig.cas.client.validation.TicketValidationException;
|
import org.jasig.cas.client.validation.TicketValidationException;
|
||||||
import org.jasig.cas.client.validation.TicketValidator;
|
import org.jasig.cas.client.validation.TicketValidator;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.cas.ServiceProperties;
|
import org.springframework.security.cas.ServiceProperties;
|
||||||
import org.springframework.security.cas.web.CasAuthenticationFilter;
|
import org.springframework.security.cas.web.CasAuthenticationFilter;
|
||||||
|
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
@ -35,6 +37,7 @@ import org.springframework.security.core.userdetails.AuthenticationUserDetailsSe
|
|||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@ -148,6 +151,87 @@ public class CasAuthenticationProviderTests {
|
|||||||
assertEquals("ST-456", newResult.getCredentials());
|
assertEquals("ST-456", newResult.getCredentials());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authenticateAllNullService() throws Exception {
|
||||||
|
String serviceUrl = "https://service/context";
|
||||||
|
ServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class);
|
||||||
|
when(details.getServiceUrl()).thenReturn(serviceUrl);
|
||||||
|
TicketValidator validator = mock(TicketValidator.class);
|
||||||
|
when(validator.validate(any(String.class),any(String.class))).thenReturn(new AssertionImpl("rod"));
|
||||||
|
|
||||||
|
ServiceProperties serviceProperties = makeServiceProperties();
|
||||||
|
serviceProperties.setAuthenticateAllArtifacts(true);
|
||||||
|
|
||||||
|
CasAuthenticationProvider cap = new CasAuthenticationProvider();
|
||||||
|
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
|
||||||
|
cap.setKey("qwerty");
|
||||||
|
|
||||||
|
cap.setTicketValidator(validator);
|
||||||
|
cap.setServiceProperties(serviceProperties);
|
||||||
|
cap.afterPropertiesSet();
|
||||||
|
|
||||||
|
String ticket = "ST-456";
|
||||||
|
UsernamePasswordAuthenticationToken token =
|
||||||
|
new UsernamePasswordAuthenticationToken(CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
|
||||||
|
|
||||||
|
Authentication result = cap.authenticate(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authenticateAllAuthenticationIsSuccessful() throws Exception {
|
||||||
|
String serviceUrl = "https://service/context";
|
||||||
|
ServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class);
|
||||||
|
when(details.getServiceUrl()).thenReturn(serviceUrl);
|
||||||
|
TicketValidator validator = mock(TicketValidator.class);
|
||||||
|
when(validator.validate(any(String.class),any(String.class))).thenReturn(new AssertionImpl("rod"));
|
||||||
|
|
||||||
|
ServiceProperties serviceProperties = makeServiceProperties();
|
||||||
|
serviceProperties.setAuthenticateAllArtifacts(true);
|
||||||
|
|
||||||
|
CasAuthenticationProvider cap = new CasAuthenticationProvider();
|
||||||
|
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
|
||||||
|
cap.setKey("qwerty");
|
||||||
|
|
||||||
|
cap.setTicketValidator(validator);
|
||||||
|
cap.setServiceProperties(serviceProperties);
|
||||||
|
cap.afterPropertiesSet();
|
||||||
|
|
||||||
|
String ticket = "ST-456";
|
||||||
|
UsernamePasswordAuthenticationToken token =
|
||||||
|
new UsernamePasswordAuthenticationToken(CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
|
||||||
|
|
||||||
|
Authentication result = cap.authenticate(token);
|
||||||
|
verify(validator).validate(ticket, serviceProperties.getService());
|
||||||
|
|
||||||
|
serviceProperties.setAuthenticateAllArtifacts(true);
|
||||||
|
result = cap.authenticate(token);
|
||||||
|
verify(validator,times(2)).validate(ticket, serviceProperties.getService());
|
||||||
|
|
||||||
|
token.setDetails(details);
|
||||||
|
result = cap.authenticate(token);
|
||||||
|
verify(validator).validate(ticket, serviceUrl);
|
||||||
|
|
||||||
|
serviceProperties.setAuthenticateAllArtifacts(false);
|
||||||
|
serviceProperties.setService(null);
|
||||||
|
cap.setServiceProperties(serviceProperties);
|
||||||
|
cap.afterPropertiesSet();
|
||||||
|
result = cap.authenticate(token);
|
||||||
|
verify(validator,times(2)).validate(ticket, serviceUrl);
|
||||||
|
|
||||||
|
token.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest()));
|
||||||
|
try {
|
||||||
|
cap.authenticate(token);
|
||||||
|
fail("Expected Exception");
|
||||||
|
}catch(IllegalStateException success) {}
|
||||||
|
|
||||||
|
cap.setServiceProperties(null);
|
||||||
|
cap.afterPropertiesSet();
|
||||||
|
try {
|
||||||
|
cap.authenticate(token);
|
||||||
|
fail("Expected Exception");
|
||||||
|
}catch(IllegalStateException success) {}
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = BadCredentialsException.class)
|
@Test(expected = BadCredentialsException.class)
|
||||||
public void missingTicketIdIsDetected() throws Exception {
|
public void missingTicketIdIsDetected() throws Exception {
|
||||||
CasAuthenticationProvider cap = new CasAuthenticationProvider();
|
CasAuthenticationProvider cap = new CasAuthenticationProvider();
|
||||||
|
@ -16,19 +16,34 @@
|
|||||||
package org.springframework.security.cas.web;
|
package org.springframework.security.cas.web;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
|
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.cas.ServiceProperties;
|
import org.springframework.security.cas.ServiceProperties;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||||
|
import org.springframework.security.web.authentication.NullRememberMeServices;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,6 +55,11 @@ import org.springframework.security.core.AuthenticationException;
|
|||||||
public class CasAuthenticationFilterTests {
|
public class CasAuthenticationFilterTests {
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGettersSetters() {
|
public void testGettersSetters() {
|
||||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||||
@ -105,6 +125,31 @@ public class CasAuthenticationFilterTests {
|
|||||||
assertFalse(filter.requiresAuthentication(request, response));
|
assertFalse(filter.requiresAuthentication(request, response));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRequiresAuthenticationAuthAll() {
|
||||||
|
ServiceProperties properties = new ServiceProperties();
|
||||||
|
properties.setAuthenticateAllArtifacts(true);
|
||||||
|
|
||||||
|
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||||
|
filter.setServiceProperties(properties);
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
request.setRequestURI(filter.getFilterProcessesUrl());
|
||||||
|
assertTrue(filter.requiresAuthentication(request, response));
|
||||||
|
|
||||||
|
request.setRequestURI("/other");
|
||||||
|
assertFalse(filter.requiresAuthentication(request, response));
|
||||||
|
request.setParameter(properties.getArtifactParameter(), "value");
|
||||||
|
assertTrue(filter.requiresAuthentication(request, response));
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(new AnonymousAuthenticationToken("key", "principal", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")));
|
||||||
|
assertTrue(filter.requiresAuthentication(request, response));
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("un", "principal", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")));
|
||||||
|
assertTrue(filter.requiresAuthentication(request, response));
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("un", "principal", "ROLE_ANONYMOUS"));
|
||||||
|
assertFalse(filter.requiresAuthentication(request, response));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAuthenticateProxyUrl() throws Exception {
|
public void testAuthenticateProxyUrl() throws Exception {
|
||||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||||
@ -117,9 +162,42 @@ public class CasAuthenticationFilterTests {
|
|||||||
assertNull(filter.attemptAuthentication(request, response));
|
assertNull(filter.attemptAuthentication(request, response));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoFilterAuthenticateAll() throws Exception {
|
||||||
|
AuthenticationSuccessHandler successHandler = mock(AuthenticationSuccessHandler.class);
|
||||||
|
AuthenticationManager manager = mock(AuthenticationManager.class);
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("un", "pwd","ROLE_USER");
|
||||||
|
when(manager.authenticate(any(Authentication.class))).thenReturn(authentication);
|
||||||
|
ServiceProperties serviceProperties = new ServiceProperties();
|
||||||
|
serviceProperties.setAuthenticateAllArtifacts(true);
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.setParameter("ticket", "ST-1-123");
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
FilterChain chain = mock(FilterChain.class);
|
||||||
|
|
||||||
|
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||||
|
filter.setServiceProperties(serviceProperties);
|
||||||
|
filter.setAuthenticationSuccessHandler(successHandler);
|
||||||
|
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
|
||||||
|
filter.setAuthenticationManager(manager);
|
||||||
|
filter.afterPropertiesSet();
|
||||||
|
|
||||||
|
filter.doFilter(request,response,chain);
|
||||||
|
assertFalse("Authentication should not be null",SecurityContextHolder.getContext().getAuthentication() == null);
|
||||||
|
verify(chain).doFilter(request, response);
|
||||||
|
verifyZeroInteractions(successHandler);
|
||||||
|
|
||||||
|
// validate for when the filterProcessUrl matches
|
||||||
|
filter.setFilterProcessesUrl(request.getRequestURI());
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
filter.doFilter(request,response,chain);
|
||||||
|
verifyNoMoreInteractions(chain);
|
||||||
|
verify(successHandler).onAuthenticationSuccess(request, response, authentication);
|
||||||
|
}
|
||||||
|
|
||||||
// SEC-1592
|
// SEC-1592
|
||||||
@Test
|
@Test
|
||||||
public void testChainNotInvokedForProxy() throws Exception {
|
public void testChainNotInvokedForProxyReceptor() throws Exception {
|
||||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
@ -35,6 +35,18 @@ public class ServicePropertiesTests {
|
|||||||
sp.afterPropertiesSet();
|
sp.afterPropertiesSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void allowNullServiceWhenAuthenticateAllTokens() throws Exception {
|
||||||
|
ServiceProperties sp = new ServiceProperties();
|
||||||
|
sp.setAuthenticateAllArtifacts(true);
|
||||||
|
sp.afterPropertiesSet();
|
||||||
|
sp.setAuthenticateAllArtifacts(false);
|
||||||
|
try {
|
||||||
|
sp.afterPropertiesSet();
|
||||||
|
fail("Expected Exception");
|
||||||
|
}catch(IllegalArgumentException success) {}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGettersSetters() throws Exception {
|
public void testGettersSetters() throws Exception {
|
||||||
ServiceProperties[] sps = {new ServiceProperties(), new SamlServiceProperties()};
|
ServiceProperties[] sps = {new ServiceProperties(), new SamlServiceProperties()};
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.cas.web.authentication;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.security.cas.ServiceProperties;
|
||||||
|
import org.springframework.security.web.util.UrlUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Rob Winch
|
||||||
|
*/
|
||||||
|
public class DefaultServiceAuthenticationDetailsTests {
|
||||||
|
private DefaultServiceAuthenticationDetails details;
|
||||||
|
private MockHttpServletRequest request;
|
||||||
|
private Pattern artifactPattern;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
request = new MockHttpServletRequest();
|
||||||
|
request.setScheme("https");
|
||||||
|
request.setServerName("localhost");
|
||||||
|
request.setServerPort(8443);
|
||||||
|
request.setRequestURI("/cas-sample/secure/");
|
||||||
|
artifactPattern = DefaultServiceAuthenticationDetails.createArtifactPattern(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getServiceUrlNullQuery() throws Exception {
|
||||||
|
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
|
||||||
|
assertEquals(UrlUtils.buildFullRequestUrl(request),details.getServiceUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getServiceUrlTicketOnlyParam() {
|
||||||
|
request.setQueryString("ticket=123");
|
||||||
|
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
|
||||||
|
String serviceUrl = details.getServiceUrl();
|
||||||
|
request.setQueryString(null);
|
||||||
|
assertEquals(UrlUtils.buildFullRequestUrl(request),serviceUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getServiceUrlTicketFirstMultiParam() {
|
||||||
|
request.setQueryString("ticket=123&other=value");
|
||||||
|
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
|
||||||
|
String serviceUrl = details.getServiceUrl();
|
||||||
|
request.setQueryString("other=value");
|
||||||
|
assertEquals(UrlUtils.buildFullRequestUrl(request),serviceUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getServiceUrlTicketLastMultiParam() {
|
||||||
|
request.setQueryString("other=value&ticket=123");
|
||||||
|
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
|
||||||
|
String serviceUrl = details.getServiceUrl();
|
||||||
|
request.setQueryString("other=value");
|
||||||
|
assertEquals(UrlUtils.buildFullRequestUrl(request),serviceUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getServiceUrlTicketMiddleMultiParam() {
|
||||||
|
request.setQueryString("other=value&ticket=123&last=this");
|
||||||
|
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
|
||||||
|
String serviceUrl = details.getServiceUrl();
|
||||||
|
request.setQueryString("other=value&last=this");
|
||||||
|
assertEquals(UrlUtils.buildFullRequestUrl(request),serviceUrl);
|
||||||
|
}
|
||||||
|
}
|
@ -215,7 +215,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
|
|||||||
chain.doFilter(request, response);
|
chain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
successfulAuthentication(request, response, authResult);
|
successfulAuthentication(request, response, chain, authResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -280,8 +280,35 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
|
|||||||
* <li>Delegates additional behaviour to the {@link AuthenticationSuccessHandler}.</li>
|
* <li>Delegates additional behaviour to the {@link AuthenticationSuccessHandler}.</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
|
* Subclasses can override this method to continue the {@link FilterChain} after successful authentication.
|
||||||
|
* @param request
|
||||||
|
* @param response
|
||||||
|
* @param chain
|
||||||
* @param authResult the object returned from the <tt>attemptAuthentication</tt> method.
|
* @param authResult the object returned from the <tt>attemptAuthentication</tt> method.
|
||||||
|
* @throws IOException
|
||||||
|
* @throws ServletException
|
||||||
*/
|
*/
|
||||||
|
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
|
||||||
|
Authentication authResult) throws IOException, ServletException{
|
||||||
|
successfulAuthentication(request, response, authResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default behaviour for successful authentication.
|
||||||
|
* <ol>
|
||||||
|
* <li>Sets the successful <tt>Authentication</tt> object on the {@link SecurityContextHolder}</li>
|
||||||
|
* <li>Invokes the configured {@link SessionAuthenticationStrategy} to handle any session-related behaviour
|
||||||
|
* (such as creating a new session to protect against session-fixation attacks).</li>
|
||||||
|
* <li>Informs the configured <tt>RememberMeServices</tt> of the successful login</li>
|
||||||
|
* <li>Fires an {@link InteractiveAuthenticationSuccessEvent} via the configured
|
||||||
|
* <tt>ApplicationEventPublisher</tt></li>
|
||||||
|
* <li>Delegates additional behaviour to the {@link AuthenticationSuccessHandler}.</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param authResult the object returned from the <tt>attemptAuthentication</tt> method.
|
||||||
|
* @deprecated since 3.1. Use {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, Authentication)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||||
Authentication authResult) throws IOException, ServletException {
|
Authentication authResult) throws IOException, ServletException {
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user