Using CAS without the Login Screen

Posted on

Using CAS without the Login Screen - Central Authentication Service - Jasig Wiki

  1. Dashboard")
  2. Central Authentication Service
  3. Home
  4. Using CAS without the Login Screen

Using CAS without the Login Screen

Skip to end of metadata

Motivation

We wanted to be more flexible in the use of the login UI, so e.g. wanted to embed it in several places as a small panel. Moreover, we wanted to understand CAS as a pure service, not having to maintain layout information twice. Also, we wanted to support both, login postings from another site and direct login at the CAS server as a fallback.

Proposed Solutions that do not work

There have been two proposals, which we haven't found to be suitable:

  • AJAX: Ajax requests are sandboxed and that sandbox is even more strict than the two-dot rule applied to cookies: It really has to be exactly the same server name in order to work
  • IFrames: In this posting to the cas-dev mailing list, it has been suggested to use an iFrame to embed a login panel on a remote site. This has two limitations:

  • How to get rid of the Frame after login? in the default view a successful login would lead to redirect only in that frame. If you use target="parent" in the from tag, how do you display errors then?

  • You would have limited layout possibilities.

    So for our requirements, none of the proposed solutions were adequate.

Our Solution

We decided to use Javascript triggered redirects, which allows us to use this mechanism also on static html pages (e.g. CMS based contents). While loading we request a login ticket, then the login form displays and then it submits to the CAS login URL. If Errors are present at CAS we redirect to the referring page, adding a parameter "error_message" to that request.

Client side

Here is the static remote login form we use: ?<!DOCTYPE html PUBLIC

"-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd"

>

Test remote Login using JS

Test remote Login using JS

Benutzer:
Password:
Login Ticket:
Service:

CAS side

Adapting only the views and introducing if-else statements there meant having logic that belongs to the controllers in the view. Therefore we decided to inject our add-on into the spring web flow. Here are the changes we have made to login-webflow.xml (changes highlighted in blue): ?<?xml version=

"1.0"

encoding=

"UTF-8"

?>

? ?

Problems encountered and suggested solutions

The solution works well, but it has a very dirty edge: introducing a new event in the submit method that allows us to distinguish between a login posting from the CAS Site and another site. Here we had to redeclare the submit method in org.jasig.cas.web.flow.AuthenticationViaFormAction (changes marked in blue): ?[...]

try

{

final

String ticketGrantingTicketId =

this

.centralAuthenticationService

.createTicketGrantingTicket(credentials);

ContextUtils.addAttribute(context,

AbstractLoginAction.REQUEST_ATTRIBUTE_TICKET_GRANTING_TICKET,

ticketGrantingTicketId);

setWarningCookie(response, warn);

return

success();

}

catch

(

final

TicketException e) {

populateErrorsInstance(context, e);

        // START: ChangesBusinessMart: check, whether the posting has been sent from a remote server
        String myServerName = request.getLocalName();
        String referrer = request.getParameter("login-at");
        if (referrer != null && referrer.indexOf(myServerName) == -1) {
            return result("errorForRemoteRequestor");
        }

?return

error();

} [...]

This could have been avoided by

  1. making this submit method overridable (non-final) to add new events in a derived class. Or
  2. to make such a distinction of events in the standard CAS distribution.

Maybe this can be accomplished somehow in future versions of CAS?

Listing of the other files mentioned in this solution

provideLoginTicketToRemoteRequestorAction -> introduces a new parameter get-lt to signal that a new login ticket shall be issued to the HTTP Referer: ?package

de.businessmart.sso.cas.flow;

import

javax.servlet.http.HttpServletRequest;

import

javax.servlet.http.HttpServletResponse;

import

org.jasig.cas.web.flow.util.ContextUtils; import

org.springframework.beans.factory.InitializingBean;

import

org.springframework.webflow.action.AbstractAction; import

org.springframework.webflow.core.collection.MutableAttributeMap;

import

org.springframework.webflow.execution.Event; import

org.springframework.webflow.execution.RequestContext;

import

de.businessmart.sso.cas.CasUtility;

///

/* Opens up the CAS web flow to allow external retrieval of a login ticket.

/* @author konrad.wulf

/*

/*/

public

class

ProvideLoginTicketToRemoteRequestorAction

extends

AbstractAction {

@Override

protected

Event doExecute(RequestContext context)

throws

Exception

{

final

HttpServletRequest request = ContextUtils.getHttpServletRequest(context);

if

(request.getParameter(

"get-lt"

) !=

null

&& request.getParameter(

"get-lt"

).equalsIgnoreCase(

"true"

)) {

return

result(

"loginTicketRequested"

);

}

return

result(

"continue"

);

}

}

viewRedirectToRequestor actually does the redirects to the referrer:

?<%

@page

import

=

"de.businessmart.sso.cas.CasUtility"

%>

<%@ taglib prefix=

"c"

uri=

"http://java.sun.com/jsp/jstl/core"

%> <%@ taglib prefix=

"spring"

uri=

"http://www.springframework.org/tags"

%>

<% String separator =

""

; String referrer = request.getHeader(

"Referer"

);

referrer = CasUtility.resetUrl(referrer); if

(referrer !=

null

&& referrer.length() >

0

) {

separator =(referrer.indexOf(

"?"

) > -

1

)?

"&"

:

"?"

; %>

<% }

else {

out.print(

"You better know what to do here."

); } %>

And for completeness, here is the CasUtility that removes obsolete parameters from the query string: ?package

de.businessmart.sso.cas;

public

class

CasUtility {

///

/* Removes the previously attached GET parameters "lt" and "error_message" to be able to send new ones.

/* @param casUrl

/* @return

/*/

public

static

String resetUrl ( String casUrl) {

String cleanedUrl;

String[] paramsToBeRemoved =

new

String[]{

"lt"

,

"error_message"

,

"get-lt"

};

cleanedUrl = removeHttpGetParameters(casUrl, paramsToBeRemoved);

return

cleanedUrl;

}

///

/* Removes selected HTTP GET parameters from a given URL

/* @param casUrl

/* @param paramsToBeRemoved

/* @return

/*/

public

static

String removeHttpGetParameters (String casUrl, String[] paramsToBeRemoved) {

String cleanedUrl = casUrl;

if

(casUrl !=

null

)

{

// check if there is any query string at all

if

(casUrl.indexOf(

"?"

) == -

1

)

{

return

casUrl;

}

else

{

// determine the start and end position of the parameters to be removed

int

startPosition, endPosition;

boolean

containsOneOfTheUnwantedParams =

false

;

for

(String paramToBeErased : paramsToBeRemoved)

{

startPosition = -

1

;

endPosition = -

1

;

if

(cleanedUrl.indexOf(

"?"

  • paramToBeErased +

"="

) > -

1

)

{

startPosition = cleanedUrl.indexOf(

"?"

  • paramToBeErased +

"="

) +

1

;

}

else

if

(cleanedUrl.indexOf(

"&"

  • paramToBeErased +

"="

) > -

1

)

{

startPosition = cleanedUrl.indexOf(

"&"

  • paramToBeErased +

"="

) +

1

;

}

if

(startPosition > -

1

)

{

int

temp = cleanedUrl.indexOf(

"&"

, startPosition);

endPosition = (temp > -

1

) ? temp +

1

: cleanedUrl

.length();

// remove that parameter, leaving the rest untouched

cleanedUrl = cleanedUrl.substring(

0

, startPosition)

  • cleanedUrl.substring(endPosition);

containsOneOfTheUnwantedParams =

true

;

}

}

// wenn nur noch das Fragezeichen vom query string übrig oder am schluss ein "&", dann auch dieses entfernen

if

(cleanedUrl.endsWith(

"?"

) || cleanedUrl.endsWith(

"&"

))

{

cleanedUrl = cleanedUrl.substring(

0

,

cleanedUrl.length() -

1

);

}

// erst zurückgeben, wenn wir auch überprüft haben, ob nicht ein parameter mehrfach angegeben wurde...

if

(!containsOneOfTheUnwantedParams)

return

casUrl;

else

cleanedUrl = removeHttpGetParameters(cleanedUrl, paramsToBeRemoved);

}

}

return

cleanedUrl;

}

} Labels:

None

Edit Labels")

8 Comments

Hide/Show Comments

  1. User icon: battags

Apr 25, 2007

Scott Battaglia

The IFRAME method actually does work. As stated in emails on the CAS developer's list, it requires replacing the default view with a JavaScript redirect view.

This allows the following: (a) errors will continue to be displayed correctly (in the iframe) (b) redirects the browser correctly (c) usernames and passwords are never collected on the client application

Jul 09, 2007

Alberto Mozzone

Hi there, I made the changes described here, but I'm stuck with these problems:

  • Where the file "viewRedirectToRequestor.jsp" must be placed ?
  • There isn't the definition of bean with class "ProvideLoginTicketToRemoteRequestorAction" in "applicationContext.xml".
  • After adding it, I get the exception:

2007-07-09 17:08:18,773 ERROR [org.apache.catalina.core.ContainerBase.[Catalina].[auth.domain.com].[/].[cas]] - Servlet.service() for servlet cas threw exception java.lang.InstantiationException at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:30)

And CAS shows its generic exception page "CAS is unavailable".

Where am I wrong ?

Somebody can help, please ?

Thanks in advance.

Alberto

Dec 07, 2007

Kevin McKee

A much simpler and elegant version is to not deal with getting a login ticket at all and to simply auto submit the CAS form via Javascript if a param such as 'auto' is submitted to the login form along with the service, username, and password parameters.

Example of casLoginView.jsp: ? <%

// If we recieve the 'auto' parameter as 'true' we attempt to // auto populate the form from the request variables and submit it

String auto = request.getParameter(

"auto"

); if

(auto !=

null

&& auto.equals(

"true"

)) {

%>

<% }

%>

No fuss, no muss. CAS will then redirect back to your service url with a ticket that is ready to be validated. This comes in really handy if you are doing something like creating users in your system and then auto logging them in after the creation process.

I know purists will hate the fact that we put a scriptlet in there (i'm not too wild about it either), but it is alot easier than all the redirects involved for grabbing a login ticket just to submit credentials.

Just a note, you will also want to change the name of the submit button from 'submit' or else Javascript will have a fit. Hope this helps.

Jun 06, 2008

Konrad Wulf

Here's a tardy response to Alberto Mozzone's post. Sorry for not having answered any earlier. But better late than never (wink) It still seems to be of interest:

I have forgotten to mention in this article that the view bmRedirectToRequestorView needs to be defined in the default_views.properties file, which is located at "/WEB-INF/classes". The following entries need to be added there:

/#/#/# Redirect with login ticket view bmRedirectToRequestorView.(class)=org.springframework.web.servlet.view.JstlView bmRedirectToRequestorView.url=/WEB-INF/view/jsp/default/ui/bmRedirectToRequestorView.jsp

Moreover, Alberto was also quite right that the bean "provideLoginTicketToRemoteRequestorAction" has to be defined somewhere. I did that in /WEB-INF/cas-servlet.xml:

id="provideLoginTicketToRemoteRequestorAction" class="de.businessmart.sso.cas.flow.ProvideLoginTicketToRemoteRequestorAction" />

With these 2 little additions the solution description should be complete.

Oct 13, 2010

gaurav saxena

Hi,

This was a nice article. But i need to do the same with that latest version of CAS i.e. 3.4 . Can you please provide me the steps for the same. Atleast the stucture for login-webflow.xml as it is now using spring-webflow-2.0.xsd .

Thanks in advance.

Gaurav Saxena

  1. User icon: eholderman

Feb 02, 2011

Ed Holderman

I have the same question. Does anyone have any tips on doing this in CAS 3.4.x and is there now a different way of accomplishing it with the new version? My end goal is to alter the Liferay login portlet to submit credentials to CAS over https. Out of the box IFrame portlets would hard code the service URL, breaking deep link capabilities. Thank you - Ed

Jun 13, 2011

Robert DeMuth

Looking to do the same thing in CAS 3.4.6 (using the Maven2 overlay method). It looks like login-webflow.xml has changed dramatically since this page was created.

  1. User icon: patino@fordham.edu

Oct 17, 2011

jpats

Hi, I just want to find out if you were able to have this working. If you did, really would like to find out how you did it. Thanks in advance.

希望本站内容对您有点用处,有什么疑问或建议请在后面留言评论
转载请注明作者(RobinChia)和出处 It so life ,请勿用于任何商业用途