I don't know if it is of any use to someone but I will detail my solution
below (using spring-security-2.0.1). This really was the only way I could
make it work as I wanted:

I added these two beans to the applicationContext-security.xml

 <bean id="CASUnprotectedPageFilter"
class="net.tootired.security.spring.cas.CASUnprotectedPageFilter">
        <property name="ignoreFilesOfType"
value="css,js,gif,jpg,png,swf,pdf" />
        <property name="loginPage" value="login!goToLogin" />
        <sec:custom-filter after="SWITCH_USER_FILTER"/>
    </bean>
    <bean id="RemoveSavedRequestKeyFilter"
class="net.tootired.security.spring.RemoveSavedRequestKeyFilter">
        <sec:custom-filter after="PRE_AUTH_FILTER"/>
    </bean>

And then the two respective classes are:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package net.tootired.security.spring.cas;

import java.util.HashSet;
import java.util.StringTokenizer;

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;

import org.springframework.web.context.support.WebApplicationContextUtils;

import org.springframework.context.ApplicationContext;

import
org.springframework.security.AuthenticationCredentialsNotFoundException;

import org.springframework.security.context.SecurityContextImpl;

import org.springframework.security.userdetails.User;

import org.springframework.security.ui.cas.ServiceProperties;

import org.springframework.security.providers.cas.CasAuthenticationToken;

import org.springframework.web.context.ServletContextAware;
        
import org.apache.log4j.Logger;

/**
 * TODO: This filter queries CAS heavily for every request (twice each
time). This will lag performace and so it may be sensible for it
 * to detect when a URL is not protected by spring-security first before
firing the requests to CAS.
 * 
 * <p>For pages that are not secured by spring-security it is not always
possible to tell if the user is loggedin. If the user logs in 
 * inside of one context and then goes to an unsecured page in another
context then the application will not know whether or not the
 * user is logged in. This fixes that by checking for a valid login for
every request.</p>
 * 
 * <p>This filter will not process requests under the following
circumstances:</p>
 * 
 * <ul>
 *  <li>If the filetype of the requested file has been passed in, as a comma
seperated list, to the setIgnoreFilesOfType(String args);</li>
 *  <li>If the requested URL is the same as setLoginPage(args)</li>
 *  <li>If this is a POST request. This restriction is so that when a form
is posted the request is not re-directed to CAS. If
 *  this was allowed to happen then when CAS processed the request and
returned us to the page we wanted to go to, we would have
 *  lost the POST data. If this is not desirable then this method could add
the posted paramters to the return URL from CAS. This
 *  would have to be done as a GET request though and so would be limited to
256 characters of data. This had not been programmed
 *  in yet and would naturally prevent file uploads and would fail for
posted data over the 256 character limit.</li>
 * </ul>
 * 
 * @author gavin
 */
public class CASUnprotectedPageFilter implements Filter ,
ServletContextAware , java.io.Serializable {

    private static final Logger logger =
Logger.getLogger(CASUnprotectedPageFilter.class);
    private ServletContext servletContext;
    private String ignoreFilesOfType; // Inputed from
applicationContext-security.xml
    private HashSet<String> ignoreFileTypes = new HashSet<String>(); // The
HashSet of the parameter taken from variable ignoreFilesOfType
    
    // The URL of the login page as used within the ROOT context. If this
filter is not running in the root context this has no effect.
    private String loginPage; 
    
    public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
        
        if(request instanceof HttpServletRequest){
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            
            // Find out the file type that was called. There is no point
asking CAS if we are logged in when we only want, for example,
            // a stylesheet, image or pdf
            String requestURL = httpRequest.getRequestURL().toString();
            int lastIndexOfDot = requestURL.lastIndexOf(".") + 1;
            String fileType = requestURL.substring(lastIndexOfDot ,
requestURL.length());
            
            // DEBUG only
            if(ignoreFileTypes.contains(fileType)) logger.debug("Will not
run filter for file of type [" + fileType +"] as it is registered to be
ignored.");
            if(requestURL.indexOf(loginPage) != -1) logger.debug("Will not
run filter as requestURL is registered as loginPage via setLoginPage(.)
method.");
            if(httpRequest.getMethod().equalsIgnoreCase("POST"))
logger.debug("Will not process filter as request was of type POST.");
            // End DEBUG only
            
            if( ! ignoreFileTypes.contains(fileType) // We don't know what
technology this service will be using so exclude the things we know we don't
want
                && 
                requestURL.indexOf(loginPage) == -1 // We don't want to see
if we are logged in when we are going to the login page
                &&
                ! httpRequest.getMethod().equalsIgnoreCase("POST")  // If we
are posting data we don't want to go to CAS as we will  
                                                                    // loose
the data on the return from CAS.
              ){
                
                logger.debug("Running filter with filetype [" + fileType
+"]");
                
                HttpSession session = httpRequest.getSession(true); 

                if(session != null){
                    boolean returnFromCas = new Boolean((String)
httpRequest.getParameter("returnFromCas")).booleanValue();
                    boolean loggedInViaCas = new Boolean((String)
httpRequest.getParameter("loggedInViaCAS")).booleanValue();

                    logger.debug("returnFromCas [" + returnFromCas +"]");
                    logger.debug("loggedInViaCas [" + loggedInViaCas +"]");

                    // Get the springContext from the session. It will be
there if we have logged in via CAS at any time (it will be there even if we
logged off)
                    SecurityContextImpl springSecurityContext =
(SecurityContextImpl) session.getAttribute("SPRING_SECURITY_CONTEXT");
                    logger.debug("springSecurityContext [" +
springSecurityContext +"]");

                    if(springSecurityContext == null){
                        springSecurityContext = new SecurityContextImpl();
                        session.setAttribute("SPRING_SECURITY_CONTEXT" ,
springSecurityContext);
                        logger.debug("Created new springSecurityContext and
added it to the session under key SPRING_SECURITY_CONTEXT");
                        logger.debug("springSecurityContext [" +
session.getAttribute("SPRING_SECURITY_CONTEXT") +"]");
                    }

                    CasAuthenticationToken casAuthenticationToken = null;
                    if(springSecurityContext != null){
                        // Get the user form the springSecurityContext, the
user will only be there if they are activly logged on through CAS
                        User user = null;
                        if(springSecurityContext != null){
                            casAuthenticationToken =
(CasAuthenticationToken) springSecurityContext.getAuthentication();
                            logger.debug("casAuthenticationToken [" +
casAuthenticationToken +"]");

                            if(casAuthenticationToken != null){
                                user = (User)
casAuthenticationToken.getPrincipal();
                            } 
                        }

                        logger.debug("user [" + user +"] <= null is OK");

                        // Allows access to beans defined in spring config
files
                        ApplicationContext applicationContext =
WebApplicationContextUtils.getWebApplicationContext(this.servletContext); //
ServletContext may be a SpringServletContext
                        logger.debug("applicationContext [" +
applicationContext +"]");

                        // Get the beans we need. They are defined in
/WEB-INF/applicationContext-security.xml
                        ServiceProperties serviceProperties =
(ServiceProperties) applicationContext.getBean("serviceProperties");
                        CasProcessingFilterEntryPoint cpfep =
(CasProcessingFilterEntryPoint)
applicationContext.getBean("casProcessingFilterEntryPoint");

                        String originalServiceURL =
serviceProperties.getService();

                        // First time into the page but only if we have
already logged into CAS 
                        // through either this or another service. This will
get us a ticket from CAS
                        if(user == null && !returnFromCas &&
!loggedInViaCas){

                            // Get the address of this page
                            String file = httpRequest.getRequestURI();
                            if (httpRequest.getQueryString() != null) file
+= '?' + httpRequest.getQueryString();
                            String thisUrl = new
java.net.URL(httpRequest.getScheme() , httpRequest.getServerName() ,
httpRequest.getServerPort() , file).toString();

                            // If not logged in then we want to use this. It
will not thow an error and is always OK but gives a 'ticket'
                            // parameter if logged into CAS.
                            String queryStringSeperator;
                            if(thisUrl.indexOf("returnFromCas=true") == -1){
                                if(thisUrl.indexOf("?") == -1)
queryStringSeperator = "?"; // Are we adding the only parameter
                                else queryStringSeperator = "&"; // or to an
already existing parameter list
                                        
                                serviceProperties.setService(thisUrl +
queryStringSeperator +"returnFromCas=true");
                                logger.debug("1. service [" +
serviceProperties.getService() +"]");
                            }

                            cpfep.setUseCASGateway(true); // Tell CAS NOT to
show us the login page if we are not logged in.
                            cpfep.commence(request, response, new
AuthenticationCredentialsNotFoundException("Authentication credentials not
found from CASUnprotectedPageFilter class."));
                            cpfep.setUseCASGateway(false); // Set this back
as we only want CAS to act as a gateway on this request
                        } 

                        // Returning from CAS possibly with a ticket
                        if(user == null && request.getParameter("ticket") !=
null && !loggedInViaCas && returnFromCas){

                            // Throws an error if we are not logged in but
if we are logged in then we will have a ticket
                           
if(originalServiceURL.indexOf("loggedInViaCAS=true") == -1)
serviceProperties.setService(serviceProperties.getService() +
"?loggedInViaCAS=true");
                            logger.debug("2. service [" +
serviceProperties.getService() +"]");
                            cpfep.setUseCASGateway(true); // Tell CAS NOT to
show us the login page if we are not logged in.
                            cpfep.commence(request, response, new
org.springframework.security.AuthenticationCredentialsNotFoundException("Authentication
object not found."));
                            cpfep.setUseCASGateway(false); // Set this back
as we only want CAS to act as a gateway on this request
                        }

                        // This must be set back with loggedInViaCas=true
                        if(originalServiceURL.indexOf("loggedInViaCAS=true")
== -1){
                            String queryStringSeperator;
                            if(originalServiceURL.indexOf("?") == -1)
queryStringSeperator = "?"; // Are we adding the only parameter
                                else queryStringSeperator = "&"; // or to an
already existing parameter list
                            serviceProperties.setService(originalServiceURL
+ queryStringSeperator + "loggedInViaCAS=true");
                        } else {
                           
serviceProperties.setService(originalServiceURL);
                        }
                    }
                }
            }
        }
        
        // Continue on with the chain
        chain.doFilter(request , response);
    }
    
    public void init(FilterConfig filterConfig) {
        this.servletContext = filterConfig.getServletContext();
    }

    public void setServletContext(ServletContext servletContext){
        this.servletContext = servletContext;
    }
    
    public void destroy() {}

    public String getIgnoreFilesOfType() {
        return ignoreFilesOfType;
    }

    /**
     * If a file of the type given here is processed through the filter then
the filter will not
     * ask CAS if the user is logged in. This is because we do not need to
know if the user
     * if logged into CAS for certain types of file to be served up to them.
If the user's browser
     * requests a css or javascript file then they can just have it, they
don't need to be logged in.
     * We can't do this the other way and allow processing for files of
type, for example .jspf as
     * the .jspf files will be included by the server and will not be send
to the filter except as part 
     * of the whole page request.
     * 
     * @param ignoreFilesOfType
     */
    public void setIgnoreFilesOfType(String ignoreFilesOfType) {
        this.ignoreFilesOfType = ignoreFilesOfType;
        
        StringTokenizer stringTokenizer = new
StringTokenizer(ignoreFilesOfType , ",");
        while(stringTokenizer.hasMoreTokens()){
            ignoreFileTypes.add(stringTokenizer.nextToken());
        }
        
        logger.debug("Will ignore files of type [" + ignoreFileTypes +"]");
    }

    /**
     * Returns the value set in applicationContext-security.xml
     * 
     * @return
     */
    public String getLoginPage() {
        return loginPage;
    }

    /**
     * This parameter should be set from the applicationContext-security.xml
file and represents a path that this filter will
     * not run for. The test used is "requestURL.indexOf(loginPage) == -1",
if true then this filter will not contact CAS.
     * 
     * @param loginPage
     */
    public void setLoginPage(String loginPage) {
        this.loginPage = loginPage;
    }
}



and 



/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package net.tootired.security.spring;

import java.io.IOException;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;

import org.springframework.security.ui.savedrequest.SavedRequest;

import org.apache.log4j.Logger;

/**
 * <p>This filter removes the SPRING_SECURITY_SAVED_REQUEST_KEY when it is
not needed. This resolves undesirable behaviour when a user
 * acts in a certain way. If a user clicks on a page that is secured by
spring-security they are taken to the CAS log in page.
 * The SPRING_SECURITY_SAVED_REQUEST_KEY into the session by spring-security
before the re-direct to CAS, they are then expected to log in.
 * If they do then they are taken 
 * back to the page defined by the SPRING_SECURITY_SAVED_REQUEST_KEY.
However, if they decide not to log in and go to another page in
 * a different sub-context (e.g. the forum if they are were about to log
into the blog), surf around on the forum for some time and then
 * decide to go back to a different page on the blog to the one they
previously visited, the SPRING_SECURITY_SAVED_REQUEST_KEY is stil
 * there. This means that even though they intended to go to a different
page in the forum they will still be taken to the first page they
 * requested as the SPRING_SECURITY_SAVED_REQUEST_KEY still points to the
first page.</p>
 * 
 * <p>This class removes the SPRING_SECURITY_SAVED_REQUEST_KEY if the user
has not come from either a page within this context or they have
 * not come from CAS as defined by the presence of either:</p>
 * 
 * <ul>
 * <li>A ticket parameter is not present in the request. A ticket may be
given by CAS and so would indicate the user has just come from
 * CAS. Therefore do not remove the SPRING_SECURITY_SAVED_REQUEST_KEY;</li>
 * <li>The presence of a returnFromCas parameter that indicates we have just
visited CAS to see if we have a valid authentication;</li>
 * <li>The presence of a loggedInViaCAS parameter which indicates that we
have just returned froma successful log in through CAS.</li>
 * </ul>
 * 
 * <p>The last two items in the above list are custom parameters defined
within this application, ticket is defined within CAS.</p>
 *
 * @author gavin
 */
public class RemoveSavedRequestKeyFilter implements Filter {

    private static final Logger logger =
Logger.getLogger(RemoveSavedRequestKeyFilter.class);
    
    public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
        
        if(request instanceof  HttpServletRequest){
        
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            HttpSession session = httpRequest.getSession(false);
            
            if(session != null &&
session.getAttribute("SPRING_SECURITY_SAVED_REQUEST_KEY") != null){
                SavedRequest savedRequest = (SavedRequest)
session.getAttribute("SPRING_SECURITY_SAVED_REQUEST_KEY");
                String savedRequestURI = savedRequest.getRequestURI();
                String httpRequestContextPath =
httpRequest.getContextPath();

                boolean returnFromCas = new
Boolean(request.getParameter("returnFromCas")).booleanValue();
                boolean loggedInViaCAS = new
Boolean(request.getParameter("loggedInViaCAS")).booleanValue();
                String casTicket = request.getParameter("ticket");

                logger.debug("savedRequest URI [" + savedRequestURI +"]");
                logger.debug("context path [" + httpRequestContextPath
+"]");
                logger.debug("returnFromCas [" + returnFromCas +"]");
                logger.debug("loggedInViaCAS [" + loggedInViaCAS +"]");
                logger.debug("Is from with context [" +
savedRequestURI.indexOf(httpRequestContextPath) +"]");
                logger.debug("casTicket [" + casTicket +"]");
                
                // See if we came form within this context or from an
external context that was not CAS
                if(savedRequestURI.indexOf(httpRequestContextPath) != -1 &&
!loggedInViaCAS && casTicket != null){
                    logger.debug("Removing SPRING_SECURITY_SAVED_REQUEST_KEY
from session");
                   
session.removeAttribute("SPRING_SECURITY_SAVED_REQUEST_KEY");
                }
            }
                
        }

        // Continue on with the chain
        chain.doFilter(request , response);
    }

    public void init(FilterConfig filterConfig) {
        
    }

    public void destroy() {
        
    }
}


I also had to override the casProcessingFilterEntryPoint in the
applicationContext-security.xml file and make it look like this so that I
could set the gateway=true parameter as it wasn't publically available in my
build of spring-security:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package net.tootired.security.spring.cas;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletResponse;

import org.jasig.cas.client.util.CommonUtils;

import org.springframework.security.AuthenticationException;

/**
 *
 * @author gavin
 */
public class CasProcessingFilterEntryPoint extends
org.springframework.security.ui.cas.CasProcessingFilterEntryPoint {

    private boolean encodeServiceUrlWithSessionId = true;
    private boolean useCASGateway = false;
    
    @Override
    public void commence(final ServletRequest servletRequest, final
ServletResponse servletResponse,
                final AuthenticationException authenticationException) throws
IOException, ServletException {

        System.out.println("*************************************");
        System.out.println("Entered
CasProcessingFilterEntryPoint.commence(...)");
        
        final HttpServletResponse httpResponse = (HttpServletResponse)
servletResponse;
        final String urlEncodedService =
CommonUtils.constructServiceUrl(null,
                                                                       
httpResponse,
                                                                       
getServiceProperties().getService(),
                                                                       
null,
                                                                       
"ticket", 
                                                                       
this.encodeServiceUrlWithSessionId);
        
        
        System.out.println("useCASGateway [" + useCASGateway +"]");
        System.out.println("urlEncodedService [" + urlEncodedService +"]");
        
        String redirectUrl = CommonUtils.constructRedirectUrl(getLoginUrl(),
                                                                   
"service",
                                                                   
urlEncodedService,
                                                                   
getServiceProperties().isSendRenew(),
                                                                   
useCASGateway);
        
        System.out.println("redirectUrl [" + redirectUrl +"]");
        
        System.out.println("*************************************");
        
        httpResponse.sendRedirect(redirectUrl);
    }

    public boolean isUseCASGateway() {
        return useCASGateway;
    }

    public void setUseCASGateway(boolean useCASGateway) {
        this.useCASGateway = useCASGateway;
    }
            
}

-- 
View this message in context: 
http://www.nabble.com/How-to-check-if-I-am-logged-into-CAS-or-not-tp18136646p18359982.html
Sent from the CAS Users mailing list archive at Nabble.com.

_______________________________________________
Yale CAS mailing list
[email protected]
http://tp.its.yale.edu/mailman/listinfo/cas

Reply via email to