Connect to the Purdue Home Page

Purdue University

Identity and Access Management

Purdue CAS BoilerKey support

  • The Purdue CAS server deployment supports the use of BoilerKey for authentication. Most deployments should be able to use a standard CAS client, with a few tweaks outlined here.
  • Use a slightly different CAS server login url: https://www.purdue.edu/apps/account/cas/login?boilerkeyRequired=true
  • Successful CAS ticket checks return an additional attribute boilerkeyauthtime, which has a format that looks like
    2012-01-19 16:37:28
  • If user is already authenticated to CAS, but not with BoilerKey or BoilerKey authentication is too old,
    invalidate the web container session, and redirect the user to https://www.purdue.edu/apps/account/cas/logout?reauthWithBoilerkeyService=<the_current_request>.

Code example in Java

web.xml
    <filter>
        <filter-name>CAS Authentication Filter With Boilerkey</filter-name>
        <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
        <init-param>
            <param-name>casServerLoginUrl</param-name>
            <param-value>https://www.purdue.edu/apps/account/cas/login?boilerkeyRequired=true</param-value>
        </init-param>
        <init-param>
            <param-name>serverName</param-name>
            <param-value>https://www.purdue.edu</param-value>
        </init-param>
    </filter>
    <filter>
        <filter-name>CAS Validation Filter</filter-name>
        <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
        <init-param>
            <param-name>casServerUrlPrefix</param-name>
            <param-value>https://www.purdue.edu/apps/account/cas</param-value>
        </init-param>
        <init-param>
            <param-name>serverName</param-name>
            <param-value>https://www.purdue.edu</param-value>
        </init-param>
        <init-param>
            <param-name>redirectAfterValidation</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>exceptionOnValidationFailure</param-name>
            <param-value>false</param-value>
        </init-param>
    </filter>
    <filter>
        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
        <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
    </filter>
    <filter>
        <filter-name>RequireBoilerkeyFilter</filter-name>
        <filter-class>edu.purdue.servlet.filter.RequireBoilerkeyFilter</filter-class>
        <init-param>
            <param-name>minutesBeforeRequiringBoilerkeyReauthentication</param-name>
            <param-value>30</param-value>
        </init-param>
        <init-param>
            <param-name>casBoilerkeyLoginUrl</param-name>
            <param-value>https://www.purdue.edu/apps/account/cas/logout?reauthWithBoilerkeyService=</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>CAS Authentication Filter With Boilerkey</filter-name>
        <url-pattern>/yourapp</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>CAS Validation Filter</filter-name>
        <url-pattern>/yourapp</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
        <url-pattern>/yourapp</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>RequireBoilerkeyFilter</filter-name>
        <url-pattern>/yourapp</url-pattern>
    </filter-mapping>
        

Servlet filter code example

(untested, please let us know if something needs changed)
package edu.purdue.servlet.filter;

import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.jasig.cas.client.authentication.AttributePrincipal;

public class RequireBoilerkeyFilter implements Filter {

	private int minutesBeforeRequiringBoilerkeyReauthentication = -1;
	private String casBoilerkeyLoginUrl;

	public void init(FilterConfig filterConfig) throws ServletException {
		if( filterConfig.getInitParameter("minutesBeforeRequiringBoilerkeyReauthentication") != null ) {
			setMinutesBeforeRequiringBoilerkeyReauthentication(
				Integer.parseInt(filterConfig.getInitParameter("minutesBeforeRequiringBoilerkeyReauthentication"))
			);
		}
		if( filterConfig.getInitParameter("casBoilerkeyLoginUrl") != null ) {
			setCasBoilerkeyLoginUrl(filterConfig.getInitParameter("casBoilerkeyLoginUrl"));
		} else if( casBoilerkeyLoginUrl == null ) {
			throw new ServletException("must set casBoilerkeyLoginUrl");
		}
	}

	public void destroy() {
	}

	private void requestBoilerkeyLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
		request.getSession().invalidate();
		response.sendRedirect(casBoilerkeyLoginUrl + request.getRequestURL());
	}

	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;

		// cas client as of 3.1.12 does not specify the Map types
		Map<?,?> attrs = ((AttributePrincipal)request.getUserPrincipal()).getAttributes();
		if( attrs == null ) {
			throw new ServletException("no attributes set by the CAS client");
		}

		if( (attrs.get("boilerkeyauthtime") != null) && (attrs.get("boilerkeyauthtime") instanceof String) ) {
			Pattern p = Pattern.compile("^(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})$");
			Matcher m = p.matcher((String)attrs.get("boilerkeyauthtime"));
			if( m.matches() ) {
				Calendar cal = Calendar.getInstance();
				cal.set(Calendar.YEAR, Integer.parseInt(m.group(1)));
				cal.set(Calendar.MONTH, Integer.parseInt(m.group(2)) - 1);
				cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(m.group(3)));
				cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m.group(4)));
				cal.set(Calendar.MINUTE, Integer.parseInt(m.group(5)));
				cal.set(Calendar.SECOND, Integer.parseInt(m.group(6)));
				cal.set(Calendar.MILLISECOND, 0);
				Date boilerKeyAuthTime = cal.getTime();

				if( minutesBeforeRequiringBoilerkeyReauthentication > -1 ) {
					Calendar nowCal = Calendar.getInstance();
					Calendar endOfSession = Calendar.getInstance();
					endOfSession.setTime(boilerKeyAuthTime);
					endOfSession.add(Calendar.MINUTE, minutesBeforeRequiringBoilerkeyReauthentication);
					if( nowCal.after(endOfSession) ) {
						// then BoilerKey was used, but enough time has passed that we need to ask for a re authentication
						requestBoilerkeyLogin(request, response);
						return;
					}
				}

			} else { // this should never happen
				requestBoilerkeyLogin(request, response);
				return;
			}

		} else { // no BoilerKey used at all
			requestBoilerkeyLogin(request, response);
			return;
		}

		// successfully authenticated with BoilerKey
		chain.doFilter(request, response);
	}

	public void setMinutesBeforeRequiringBoilerkeyReauthentication(
			int minutesBeforeRequiringBoilerkeyReauthentication) {
		this.minutesBeforeRequiringBoilerkeyReauthentication = minutesBeforeRequiringBoilerkeyReauthentication;
	}

	public void setCasBoilerkeyLoginUrl(String casBoilerkeyLoginUrl) {
		this.casBoilerkeyLoginUrl = casBoilerkeyLoginUrl;
	}
}
        

Questions

Please contact accounts@purdue.edu.

Feedback | Contact Purdue | Style Standards
Maintained by: IAMO Team

Purdue University, West Lafayette, IN 47907, (765) 494-4600
© 2010 - 2013 Purdue University | An equal access/equal opportunity university | Copyright Complaints
If you have trouble accessing this page because of a disability, please contact the CSC at itap@purdue.edu or (765) 494-4000.